aboutsummaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/taler-merchant-httpd.c')
-rw-r--r--src/backend/taler-merchant-httpd.c474
1 files changed, 8 insertions, 466 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index d48c2f92..f95beb3e 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -137,23 +137,6 @@ static int merchant_connection_close;
137static int result; 137static int result;
138 138
139/** 139/**
140 * MIN-Heap of suspended connections to resume when the timeout expires,
141 * ordered by timeout. Values are of type `struct MHD_Connection`
142 */
143static struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
144
145/**
146 * Hash map from H(order_id,merchant_pub) to `struct MHD_Connection`
147 * entries to resume when a payment is made for the given order.
148 */
149static struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
150
151/**
152 * Task responsible for timeouts in the #resume_timeout_heap.
153 */
154static struct GNUNET_SCHEDULER_Task *resume_timeout_task;
155
156/**
157 * Our configuration. 140 * Our configuration.
158 */ 141 */
159static const struct GNUNET_CONFIGURATION_Handle *cfg; 142static const struct GNUNET_CONFIGURATION_Handle *cfg;
@@ -164,24 +147,6 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
164char *TMH_default_auth; 147char *TMH_default_auth;
165 148
166 149
167/**
168 * Holds data needed to determine when to resume a connection for
169 * GET /orders/$ORDER_ID
170 */
171struct ResumeData
172{
173 /**
174 * How much of the order has been refunded.
175 */
176 const struct TALER_Amount *refund_amount;
177
178 /**
179 * Whether the refunds for the order were obtained.
180 */
181 bool obtained;
182};
183
184
185int 150int
186TMH_check_auth (const char *token, 151TMH_check_auth (const char *token,
187 const struct GNUNET_ShortHashCode *salt, 152 const struct GNUNET_ShortHashCode *salt,
@@ -292,393 +257,6 @@ TMH_instance_free_cb (void *cls,
292 257
293 258
294/** 259/**
295 * Callback that frees all the elements in the #payment_trigger_map.
296 * This function should actually never be called, as by the time we
297 * get to it, all payment triggers should have been cleaned up!
298 *
299 * @param cls closure, NULL
300 * @param key current key
301 * @param value a `struct TMH_SuspendedConnection`
302 * @return #GNUNET_OK
303 */
304static int
305payment_trigger_free (void *cls,
306 const struct GNUNET_HashCode *key,
307 void *value)
308{
309 struct TMH_SuspendedConnection *sc = value;
310
311 (void) cls;
312 (void) key;
313 (void) sc; /* cannot really 'clean up' */
314 GNUNET_break (0);
315 return GNUNET_OK;
316}
317
318
319/**
320 * Compute @a key to use for @a order_id and @a mpub in our
321 * #payment_trigger_map.
322 *
323 * @param order_id an order ID
324 * @param mpub an instance public key
325 * @param[out] key set to the hash map key to use
326 */
327static void
328compute_pay_key (const char *order_id,
329 const struct TALER_MerchantPublicKeyP *mpub,
330 struct GNUNET_HashCode *key)
331{
332 size_t olen = strlen (order_id);
333 char buf[sizeof (*mpub) + olen];
334
335 /* sanity check for arithmetic overflow */
336 GNUNET_assert (olen < 1024 * 1024);
337 memcpy (buf,
338 mpub,
339 sizeof (*mpub));
340 memcpy (&buf[sizeof (*mpub)],
341 order_id,
342 olen);
343 GNUNET_CRYPTO_hash (buf,
344 sizeof (buf),
345 key);
346 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347 "Pay key for `%s' is %s\n",
348 order_id,
349 GNUNET_h2s (key));
350}
351
352
353/**
354 * Compute @a key to use for @a session_id and @a fulfillment_url in our
355 * #payment_trigger_map.
356 *
357 * @param session_id the session for which @a fulfillment_url matters
358 * @param fulfillment_url fullfillment URL of an order
359 * @param[out] key set to the hash map key to use
360 */
361static void
362compute_pay_key2 (const char *session_id,
363 const char *fulfillment_url,
364 struct GNUNET_HashCode *key)
365{
366 size_t slen = strlen (session_id) + 1;
367 size_t ulen = strlen (fulfillment_url) + 1;
368 char buf[slen + ulen];
369
370 memcpy (buf,
371 session_id,
372 slen);
373 memcpy (&buf[slen],
374 fulfillment_url,
375 ulen);
376 GNUNET_CRYPTO_hash (buf,
377 sizeof (buf),
378 key);
379 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
380 "Pay key2 for %s/%s is %s\n",
381 session_id,
382 fulfillment_url,
383 GNUNET_h2s (key));
384}
385
386
387/**
388 * Resume processing all suspended connections past timeout.
389 *
390 * @param cls unused
391 */
392static void
393do_resume (void *cls)
394{
395 struct TMH_SuspendedConnection *sc;
396
397 (void) cls;
398 resume_timeout_task = NULL;
399 while (1)
400 {
401 sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
402 if (NULL == sc)
403 return;
404 if (GNUNET_TIME_absolute_is_future (sc->long_poll_timeout))
405 break;
406 GNUNET_assert (sc ==
407 GNUNET_CONTAINER_heap_remove_root (resume_timeout_heap));
408 sc->hn = NULL;
409 GNUNET_assert (GNUNET_YES ==
410 GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
411 &sc->key,
412 sc));
413 if (sc->has_key2)
414 GNUNET_assert (GNUNET_YES ==
415 GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
416 &sc->key2,
417 sc));
418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419 "Resuming long polled job due to timeout\n");
420 MHD_resume_connection (sc->con);
421 TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
422 }
423 if (NULL != resume_timeout_task)
424 GNUNET_SCHEDULER_cancel (resume_timeout_task);
425 resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
426 &do_resume,
427 NULL);
428}
429
430
431/**
432 * Suspend connection from @a sc until payment has been received.
433 *
434 * @param order_id the order that we are waiting on
435 * @param session_id session ID of the requester
436 * @param fulfillment_url fulfillment URL of the contract
437 * @param mi the merchant instance we are waiting on
438 * @param sc connection to suspend
439 * @param min_refund refund amount we are waiting on to be exceeded before resuming,
440 * NULL if we are not waiting for refunds
441 */
442void
443TMH_long_poll_suspend (const char *order_id,
444 const char *session_id,
445 const char *fulfillment_url,
446 const struct TMH_MerchantInstance *mi,
447 struct TMH_SuspendedConnection *sc,
448 const struct TALER_Amount *min_refund)
449{
450 compute_pay_key (order_id,
451 &mi->merchant_pub,
452 &sc->key);
453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
454 "Suspending operation on key %s\n",
455 GNUNET_h2s (&sc->key));
456 GNUNET_assert (GNUNET_OK ==
457 GNUNET_CONTAINER_multihashmap_put (payment_trigger_map,
458 &sc->key,
459 sc,
460 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
461 if ( (NULL != session_id) &&
462 (NULL != fulfillment_url) )
463 {
464 sc->has_key2 = true;
465 compute_pay_key2 (session_id,
466 fulfillment_url,
467 &sc->key2);
468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
469 "Suspending operation on %s/%s key2 %s\n",
470 session_id,
471 fulfillment_url,
472 GNUNET_h2s (&sc->key2));
473 GNUNET_assert (GNUNET_OK ==
474 GNUNET_CONTAINER_multihashmap_put (payment_trigger_map,
475 &sc->key2,
476 sc,
477 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
478 }
479 if (NULL != min_refund)
480 {
481 sc->awaiting_refund = true;
482 sc->refund_expected = *min_refund;
483 }
484 sc->hn = GNUNET_CONTAINER_heap_insert (resume_timeout_heap,
485 sc,
486 sc->long_poll_timeout.abs_value_us);
487 MHD_suspend_connection (sc->con);
488 if (NULL != resume_timeout_task)
489 {
490 GNUNET_SCHEDULER_cancel (resume_timeout_task);
491 resume_timeout_task = NULL;
492 }
493 sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
494 resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
495 &do_resume,
496 NULL);
497}
498
499
500/**
501 * Function called to resume suspended connections.
502 *
503 * @param cls pointer to a `struct TALER_Amount` indicating the refund amount, or NULL
504 * @param key key in the #payment_trigger_map
505 * @param value a `struct TMH_SuspendedConnection` to resume
506 * @return #GNUNET_OK (continue to iterate)
507 */
508static int
509resume_operation (void *cls,
510 const struct GNUNET_HashCode *key,
511 void *value)
512{
513 const struct ResumeData *rd = cls;
514 struct TMH_SuspendedConnection *sc = value;
515
516 GNUNET_assert (0 ==
517 GNUNET_memcmp (key,
518 &sc->key));
519 /* If the conditions are satisfied partially, turn them off for future
520 calls. */
521 if ( (sc->awaiting_refund_obtained) &&
522 (rd->obtained))
523 sc->awaiting_refund_obtained = false;
524 if ( (sc->awaiting_refund) &&
525 ( (NULL != rd->refund_amount) &&
526 (1 == TALER_amount_cmp (rd->refund_amount,
527 &sc->refund_expected)) ) )
528 sc->awaiting_refund = false;
529
530 if ( (sc->awaiting_refund_obtained) &&
531 (! rd->obtained))
532 {
533 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
534 "Not awaking client, refunds not yet obtained\n");
535 return GNUNET_OK;
536 }
537 if ( (sc->awaiting_refund) &&
538 ( (NULL == rd->refund_amount) ||
539 (1 != TALER_amount_cmp (rd->refund_amount,
540 &sc->refund_expected)) ) )
541 {
542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
543 "Not awaking client, refund amount of %s not yet satisfied\n",
544 TALER_amount2s (&sc->refund_expected));
545 return GNUNET_OK; /* skip */
546 }
547 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
548 "Resuming operation suspended pending payment on key %s\n",
549 GNUNET_h2s (key));
550 GNUNET_assert (GNUNET_YES ==
551 GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
552 key,
553 sc));
554 if (sc->has_key2)
555 GNUNET_assert (GNUNET_YES ==
556 GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
557 &sc->key2,
558 sc));
559 GNUNET_assert (sc ==
560 GNUNET_CONTAINER_heap_remove_node (sc->hn));
561 sc->hn = NULL;
562 MHD_resume_connection (sc->con);
563 TALER_MHD_daemon_trigger ();
564 return GNUNET_OK;
565}
566
567
568/**
569 * Find out if we have any clients long-polling for @a order_id to be
570 * confirmed at merchant @a mpub, and if so, tell them to resume.
571 *
572 * @param order_id the order that was paid or refunded
573 * @param mi the merchant instance where the payment or refund happened
574 * @param refund_amount refunded amount, if the trigger was a refund, otherwise NULL
575 * @param obtained if true, the wallet has obtained the refunds for the order
576 */
577void
578TMH_long_poll_resume (const char *order_id,
579 const struct TMH_MerchantInstance *mi,
580 const struct TALER_Amount *refund_amount,
581 bool obtained)
582{
583 struct GNUNET_HashCode key;
584 struct ResumeData rd = {
585 .refund_amount = refund_amount,
586 .obtained = obtained
587 };
588 int ret;
589
590 compute_pay_key (order_id,
591 &mi->merchant_pub,
592 &key);
593 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
594 "Resuming operations suspended pending payment on key %s up to refund %s\n",
595 GNUNET_h2s (&key),
596 (NULL != refund_amount)
597 ? TALER_amount2s (refund_amount)
598 : "<none>");
599 ret = GNUNET_CONTAINER_multihashmap_get_multiple (payment_trigger_map,
600 &key,
601 &resume_operation,
602 (void *) &rd);
603 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
604 "%u operations remain suspended pending payment (%d)\n",
605 GNUNET_CONTAINER_multihashmap_size (payment_trigger_map),
606 ret);
607}
608
609
610/**
611 * Function called to resume suspended connections.
612 *
613 * @param cls NULL
614 * @param key key in the #payment_trigger_map
615 * @param value a `struct TMH_SuspendedConnection` to resume
616 * @return #GNUNET_OK (continue to iterate)
617 */
618static int
619resume_operation2 (void *cls,
620 const struct GNUNET_HashCode *key,
621 void *value)
622{
623 struct TMH_SuspendedConnection *sc = value;
624
625 GNUNET_assert (sc->has_key2);
626 GNUNET_assert (0 == GNUNET_memcmp (key,
627 &sc->key2));
628 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629 "Resuming operation suspended pending payment on key %s\n",
630 GNUNET_h2s (key));
631 GNUNET_assert (GNUNET_YES ==
632 GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
633 &sc->key,
634 sc));
635 GNUNET_assert (GNUNET_YES ==
636 GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
637 &sc->key2,
638 sc));
639 GNUNET_assert (sc ==
640 GNUNET_CONTAINER_heap_remove_node (sc->hn));
641 sc->hn = NULL;
642 MHD_resume_connection (sc->con);
643 TALER_MHD_daemon_trigger ();
644 return GNUNET_OK;
645}
646
647
648/**
649 * Find out if we have any clients long-polling for @a order_id to be
650 * confirmed at merchant @a mpub, and if so, tell them to resume.
651 *
652 * @param session_id the session for which @a fulfillment_url became paid
653 * @param fulfillment_url fullfillment URL of which an order was paid
654 */
655void
656TMH_long_poll_resume2 (const char *session_id,
657 const char *fulfillment_url)
658{
659 struct GNUNET_HashCode key;
660 int ret;
661
662 compute_pay_key2 (session_id,
663 fulfillment_url,
664 &key);
665 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
666 "Resuming operations suspended pending payment on %s/%s with key2 %s\n",
667 session_id,
668 fulfillment_url,
669 GNUNET_h2s (&key));
670 ret = GNUNET_CONTAINER_multihashmap_get_multiple (payment_trigger_map,
671 &key,
672 &resume_operation2,
673 NULL);
674 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
675 "%u operations remain suspended pending payment (%d)\n",
676 GNUNET_CONTAINER_multihashmap_size (payment_trigger_map),
677 ret);
678}
679
680
681/**
682 * Shutdown task (magically invoked when the application is being 260 * Shutdown task (magically invoked when the application is being
683 * quit) 261 * quit)
684 * 262 *
@@ -687,9 +265,6 @@ TMH_long_poll_resume2 (const char *session_id,
687static void 265static void
688do_shutdown (void *cls) 266do_shutdown (void *cls)
689{ 267{
690 struct TMH_SuspendedConnection *sc;
691 struct MHD_Daemon *mhd;
692
693 (void) cls; 268 (void) cls;
694 TMH_force_ac_resume (); 269 TMH_force_ac_resume ();
695 TMH_force_pc_resume (); 270 TMH_force_pc_resume ();
@@ -698,27 +273,12 @@ do_shutdown (void *cls)
698 TMH_force_tip_pickup_resume (); 273 TMH_force_tip_pickup_resume ();
699 TMH_force_wallet_get_order_resume (); 274 TMH_force_wallet_get_order_resume ();
700 TMH_force_wallet_refund_order_resume (); 275 TMH_force_wallet_refund_order_resume ();
701 mhd = TALER_MHD_daemon_stop ();
702 /* resume all suspended connections, must be done before stopping #mhd */
703 if (NULL != resume_timeout_heap)
704 { 276 {
705 while (NULL != (sc = GNUNET_CONTAINER_heap_remove_root ( 277 struct MHD_Daemon *mhd;
706 resume_timeout_heap))) 278
707 { 279 mhd = TALER_MHD_daemon_stop ();
708 sc->hn = NULL; 280 if (NULL != mhd)
709 GNUNET_assert (GNUNET_YES == 281 MHD_stop_daemon (mhd);
710 GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
711 &sc->key,
712 sc));
713 MHD_resume_connection (sc->con);
714 }
715 GNUNET_CONTAINER_heap_destroy (resume_timeout_heap);
716 resume_timeout_heap = NULL;
717 }
718 if (NULL != mhd)
719 {
720 MHD_stop_daemon (mhd);
721 mhd = NULL;
722 } 282 }
723 TMH_RESERVES_done (); 283 TMH_RESERVES_done ();
724 if (NULL != instance_eh) 284 if (NULL != instance_eh)
@@ -733,19 +293,6 @@ do_shutdown (void *cls)
733 } 293 }
734 TMH_EXCHANGES_done (); 294 TMH_EXCHANGES_done ();
735 TMH_AUDITORS_done (); 295 TMH_AUDITORS_done ();
736 if (NULL != resume_timeout_task)
737 {
738 GNUNET_SCHEDULER_cancel (resume_timeout_task);
739 resume_timeout_task = NULL;
740 }
741 if (NULL != payment_trigger_map)
742 {
743 GNUNET_CONTAINER_multihashmap_iterate (payment_trigger_map,
744 &payment_trigger_free,
745 NULL);
746 GNUNET_CONTAINER_multihashmap_destroy (payment_trigger_map);
747 payment_trigger_map = NULL;
748 }
749 if (NULL != TMH_by_id_map) 296 if (NULL != TMH_by_id_map)
750 { 297 {
751 GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map, 298 GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
@@ -2019,7 +1566,7 @@ load_instances (void *cls,
2019 ( (0 == extra_len) || 1566 ( (0 == extra_len) ||
2020 ('\0' != id[extra_len - 1]) ) ) 1567 ('\0' != id[extra_len - 1]) ) )
2021 { 1568 {
2022 GNUNET_break (0); /* bogus notification */ 1569 GNUNET_break (0 == extra_len);
2023 extra = NULL; 1570 extra = NULL;
2024 } 1571 }
2025 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1572 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -2073,7 +1620,7 @@ TMH_reload_instances (const char *id)
2073{ 1620{
2074 struct GNUNET_DB_EventHeaderP es = { 1621 struct GNUNET_DB_EventHeaderP es = {
2075 es.size = ntohs (sizeof (es)), 1622 es.size = ntohs (sizeof (es)),
2076 es.type = ntohs (sizeof (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)) 1623 es.type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
2077 }; 1624 };
2078 1625
2079 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1626 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -2127,11 +1674,6 @@ run (void *cls,
2127 result = GNUNET_SYSERR; 1674 result = GNUNET_SYSERR;
2128 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1675 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2129 NULL); 1676 NULL);
2130 resume_timeout_heap
2131 = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
2132 payment_trigger_map
2133 = GNUNET_CONTAINER_multihashmap_create (16,
2134 GNUNET_YES);
2135 if (GNUNET_OK != 1677 if (GNUNET_OK !=
2136 TALER_config_get_currency (cfg, 1678 TALER_config_get_currency (cfg,
2137 &TMH_currency)) 1679 &TMH_currency))
@@ -2209,7 +1751,7 @@ run (void *cls,
2209 { 1751 {
2210 struct GNUNET_DB_EventHeaderP es = { 1752 struct GNUNET_DB_EventHeaderP es = {
2211 es.size = ntohs (sizeof (es)), 1753 es.size = ntohs (sizeof (es)),
2212 es.type = ntohs (sizeof (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)) 1754 es.type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
2213 }; 1755 };
2214 1756
2215 instance_eh = TMH_db->event_listen (TMH_db->cls, 1757 instance_eh = TMH_db->event_listen (TMH_db->cls,