From 590bb2c6d181bd703ff4b1c528662898f28eab79 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 23 Jul 2021 17:07:51 +0200 Subject: -misc backend changes from workshop --- src/backend/taler-merchant-httpd.c | 1 + src/backend/taler-merchant-httpd_get-orders-ID.c | 313 ++++++++++++--------- src/backend/taler-merchant-httpd_get-orders-ID.h | 7 + .../taler-merchant-httpd_private-get-orders-ID.c | 11 + 4 files changed, 194 insertions(+), 138 deletions(-) (limited to 'src') diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 277f7480..ac223a5b 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -703,6 +703,7 @@ do_shutdown (void *cls) TMH_force_rc_resume (); TMH_force_post_transfers_resume (); TMH_force_tip_pickup_resume (); + TMH_force_wallet_get_order_resume (); TMH_force_wallet_refund_order_resume (); if (NULL != mhd_task) { diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c index 110333fc..b061ff43 100644 --- a/src/backend/taler-merchant-httpd_get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -119,7 +119,8 @@ struct GetOrderData enum TALER_ErrorCode ec; /** - * Did we suspend @a connection? + * Did we suspend @a connection and are thus in + * the #god_head DLL? */ bool suspended; @@ -161,9 +162,6 @@ static struct GetOrderData *god_head; static struct GetOrderData *god_tail; -/** - * Force resuming all suspended order lookups, needed during shutdown. - */ void TMH_force_wallet_get_order_resume (void) { @@ -192,6 +190,11 @@ suspend_god (struct GetOrderData *god) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Suspending GET /orders/%s\n", god->order_id); + GNUNET_assert (! god->suspended); + god->suspended = true; + GNUNET_CONTAINER_DLL_insert (god_head, + god_tail, + god); TMH_long_poll_suspend (god->order_id, god->session_id, god->fulfillment_url, @@ -231,7 +234,6 @@ make_taler_refund_uri (const char *merchant_base_url, return NULL; } GNUNET_assert (NULL != order_id); - GNUNET_buffer_write_str (&buf, "taler"); if (0 == strcasecmp ("http", @@ -275,23 +277,25 @@ TMH_make_order_status_url (struct MHD_Connection *con, host = MHD_lookup_connection_value (con, MHD_HEADER_KIND, - "Host"); + MHD_HTTP_HEADER_HOST); forwarded_host = MHD_lookup_connection_value (con, MHD_HEADER_KIND, "X-Forwarded-Host"); - uri_path = MHD_lookup_connection_value (con, MHD_HEADER_KIND, "X-Forwarded-Prefix"); if (NULL != forwarded_host) host = forwarded_host; - if (NULL == host) { GNUNET_break (0); return NULL; } - + if (NULL != strchr (host, '/')) + { + GNUNET_break_op (0); + return NULL; + } GNUNET_assert (NULL != instance_id); GNUNET_assert (NULL != order_id); @@ -301,7 +305,6 @@ TMH_make_order_status_url (struct MHD_Connection *con, else GNUNET_buffer_write_str (&buf, "https://"); - GNUNET_buffer_write_str (&buf, host); if (NULL != uri_path) @@ -322,6 +325,7 @@ TMH_make_order_status_url (struct MHD_Connection *con, if ((NULL != claim_token) && (GNUNET_NO == GNUNET_is_zero (claim_token))) { + /* 'token=' for human readability */ GNUNET_buffer_write_str (&buf, "?token="); GNUNET_buffer_write_data_encoded (&buf, @@ -374,26 +378,27 @@ TMH_make_taler_pay_uri (struct MHD_Connection *con, host = MHD_lookup_connection_value (con, MHD_HEADER_KIND, - "Host"); + MHD_HTTP_HEADER_HOST); forwarded_host = MHD_lookup_connection_value (con, MHD_HEADER_KIND, "X-Forwarded-Host"); - uri_path = MHD_lookup_connection_value (con, MHD_HEADER_KIND, "X-Forwarded-Prefix"); if (NULL != forwarded_host) host = forwarded_host; - if (NULL == host) { GNUNET_break (0); return NULL; } - + if (NULL != strchr (host, '/')) + { + GNUNET_break_op (0); + return NULL; + } GNUNET_assert (NULL != instance_id); GNUNET_assert (NULL != order_id); - GNUNET_buffer_write_str (&buf, "taler"); if (GNUNET_NO == TALER_mhd_is_https (con)) @@ -421,6 +426,8 @@ TMH_make_taler_pay_uri (struct MHD_Connection *con, if ((NULL != claim_token) && (GNUNET_NO == GNUNET_is_zero (claim_token))) { + /* Just 'c=' because this goes into QR + codes, so this is more compact. */ GNUNET_buffer_write_str (&buf, "?c="); GNUNET_buffer_write_data_encoded (&buf, @@ -437,10 +444,10 @@ TMH_make_taler_pay_uri (struct MHD_Connection *con, * preferred language of the HTTP client. * * @param god order to extract summary from - * @return NULL if no summary was provided in the contract + * @return dummy error message summary if no summary was provided in the contract */ static const char * -get_order_summary (struct GetOrderData *god) +get_order_summary (const struct GetOrderData *god) { const char *language_pattern; const char *ret; @@ -455,8 +462,10 @@ get_order_summary (struct GetOrderData *god) "summary")); if (NULL == ret) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No order summary found!\n"); + /* Upon order creation (and insertion into the database), the presence + of a summary should have been checked. So if we get here, someone + did something fishy to our database... */ + GNUNET_break (0); ret = ""; } return ret; @@ -483,8 +492,7 @@ send_pay_request (struct GetOrderData *god, remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout); if ( (0 != remaining.rel_value_us) && - ( (NULL == already_paid_order_id) || - (NULL == god->fulfillment_url) ) ) + (NULL == already_paid_order_id) ) { /* long polling: do not queue a response, suspend connection instead */ suspend_god (god); @@ -507,19 +515,28 @@ send_pay_request (struct GetOrderData *god, god->hc->instance->settings.id, &god->claim_token, NULL); + if ( (NULL == taler_pay_uri) || + (NULL == order_status_url) ) + { + GNUNET_break_op (0); + GNUNET_free (taler_pay_uri); + GNUNET_free (order_status_url); + return TALER_MHD_reply_with_error (god->sc.con, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, + "host"); + } if (god->generate_html) { - char *qr; - - if ( (NULL != already_paid_order_id) && - (NULL != god->fulfillment_url) ) + if (NULL != already_paid_order_id) { struct MHD_Response *reply; - MHD_RESULT ret; + GNUNET_assert (NULL != god->fulfillment_url); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Redirecting to already paid order %s\n", - already_paid_order_id); + "Redirecting to already paid order %s via fulfillment URL %s\n", + already_paid_order_id, + god->fulfillment_url); reply = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); @@ -532,50 +549,61 @@ send_pay_request (struct GetOrderData *god, MHD_add_response_header (reply, MHD_HTTP_HEADER_LOCATION, god->fulfillment_url)); - ret = MHD_queue_response (god->sc.con, - MHD_HTTP_FOUND, - reply); - MHD_destroy_response (reply); - return ret; - } + { + MHD_RESULT ret; - qr = TMH_create_qrcode (taler_pay_uri); - if (NULL == qr) - { - GNUNET_break (0); - return MHD_NO; + ret = MHD_queue_response (god->sc.con, + MHD_HTTP_FOUND, + reply); + MHD_destroy_response (reply); + return ret; + } } + { - enum GNUNET_GenericReturnValue res; - json_t *context; + char *qr; - context = json_pack ("{s:s, s:s, s:s, s:s}", - "taler_pay_uri", - taler_pay_uri, - "order_status_url", - order_status_url, - "taler_pay_qrcode_svg", - qr, - "order_summary", - get_order_summary (god)); - GNUNET_assert (NULL != context); - res = TMH_return_from_template (god->sc.con, - MHD_HTTP_PAYMENT_REQUIRED, - "request_payment", - god->hc->instance->settings.id, - taler_pay_uri, - context); - if (GNUNET_SYSERR == res) + qr = TMH_create_qrcode (taler_pay_uri); + if (NULL == qr) { GNUNET_break (0); - ret = MHD_NO; + return MHD_NO; } - ret = MHD_YES; - json_decref (context); + { + enum GNUNET_GenericReturnValue res; + json_t *context; + + context = json_pack ("{s:s, s:s, s:s, s:s}", + "taler_pay_uri", + taler_pay_uri, + "order_status_url", + order_status_url, + "taler_pay_qrcode_svg", + qr, + "order_summary", + get_order_summary (god)); + GNUNET_assert (NULL != context); + res = TMH_return_from_template (god->sc.con, + MHD_HTTP_PAYMENT_REQUIRED, + "request_payment", + god->hc->instance->settings.id, + taler_pay_uri, + context); + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + ret = MHD_NO; + } + else + { + ret = MHD_YES; + } + json_decref (context); + } + GNUNET_free (qr); } - GNUNET_free (qr); } - else + else /* end of 'generate HTML' */ { ret = TALER_MHD_reply_json_pack (god->sc.con, MHD_HTTP_PAYMENT_REQUIRED, @@ -586,7 +614,6 @@ send_pay_request (struct GetOrderData *god, god->fulfillment_url, "already_paid_order_id", already_paid_order_id); - } GNUNET_free (taler_pay_uri); GNUNET_free (order_status_url); @@ -626,6 +653,7 @@ process_refunds_cb (void *cls, TALER_amount2s (refund_amount), TALER_B2S (coin_pub), reason); + god->refund_available |= pending; if (god->refunded) { GNUNET_assert (0 <= @@ -636,7 +664,6 @@ process_refunds_cb (void *cls, } god->refund_amount = *refund_amount; god->refunded = true; - god->refund_available |= pending; } @@ -794,6 +821,14 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, } } /* end of first-time initialization / sanity checks */ + if (god->suspended) + { + god->suspended = false; + GNUNET_CONTAINER_DLL_remove (god_head, + god_tail, + god); + } + /* Convert order_id to h_contract_terms */ TMH_db->preflight (TMH_db->cls); if (NULL == god->contract_terms) @@ -1006,7 +1041,14 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, if ( (NULL != god->session_id) && (NULL != god->fulfillment_url) ) { - /* Check if paid within a session. */ + /* Check if client paid for this fulfillment article + already within this session, but using a different + order ID. If so, redirect the client to the order + it already paid. Allows, for example, the case + where a mobile phone pays for a browser's session, + where the mobile phone has a different order + ID (because it purchased the article earlier) + than the one that the browser is waiting for. */ char *already_paid_order_id = NULL; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -1045,7 +1087,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, GNUNET_free (already_paid_order_id); return ret; } - GNUNET_break (1 == qs); + GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); GNUNET_free (already_paid_order_id); } @@ -1066,7 +1108,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, - "order status"); + "lookup_order_status"); } GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); if (! paid) @@ -1095,7 +1137,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, - "refunds, detailed"); + "lookup_refunds_detailed"); } if ( ((god->sc.awaiting_refund) && @@ -1126,99 +1168,94 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, } /* All operations done, build final response */ + if (god->generate_html) { - if (god->generate_html) + enum GNUNET_GenericReturnValue res; + + if (god->refund_available) { - enum GNUNET_GenericReturnValue res; + char *qr; + char *uri; - if (god->refund_available) + GNUNET_assert (NULL != god->contract_terms); + uri = make_taler_refund_uri (merchant_base_url, + order_id); + if (NULL == uri) { - char *qr; - char *uri; - - GNUNET_assert (NULL != god->contract_terms); - uri = make_taler_refund_uri (merchant_base_url, - order_id); - if (NULL == uri) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (god->sc.con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_ALLOCATION_FAILURE, - "refund URI"); - } - qr = TMH_create_qrcode (uri); - if (NULL == qr) - { - GNUNET_break (0); - GNUNET_free (uri); - return TALER_MHD_reply_with_error (god->sc.con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_ALLOCATION_FAILURE, - "qr code"); - } - { - json_t *context; - - context = json_pack ("{s:s, s:s, s:s, s:s}", - "order_summary", - get_order_summary (god), - "refund_amount", - TALER_amount2s (&god->refund_amount), - "taler_refund_uri", - uri, - "taler_refund_qrcode_svg", - qr); - GNUNET_assert (NULL != context); - res = TMH_return_from_template (god->sc.con, - MHD_HTTP_OK, - "offer_refund", - hc->instance->settings.id, - uri, - context); - json_decref (context); - } + GNUNET_break (0); + return TALER_MHD_reply_with_error (god->sc.con, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_ALLOCATION_FAILURE, + "refund URI"); + } + qr = TMH_create_qrcode (uri); + if (NULL == qr) + { + GNUNET_break (0); GNUNET_free (uri); - GNUNET_free (qr); + return TALER_MHD_reply_with_error (god->sc.con, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_ALLOCATION_FAILURE, + "qr code"); } - else { json_t *context; - context = json_pack ("{s:O, s:s, s:s}", - "contract_terms", - god->contract_terms, + context = json_pack ("{s:s, s:s, s:s, s:s}", "order_summary", get_order_summary (god), "refund_amount", - TALER_amount2s (&god->refund_amount)); + TALER_amount2s (&god->refund_amount), + "taler_refund_uri", + uri, + "taler_refund_qrcode_svg", + qr); GNUNET_assert (NULL != context); res = TMH_return_from_template (god->sc.con, MHD_HTTP_OK, - "show_order_details", + "offer_refund", hc->instance->settings.id, - NULL, + uri, context); json_decref (context); } - if (GNUNET_SYSERR == res) - { - GNUNET_break (0); - return MHD_NO; - } - return MHD_YES; + GNUNET_free (uri); + GNUNET_free (qr); } else { - return TALER_MHD_reply_json_pack ( - connection, - MHD_HTTP_OK, - "{s:b, s:b, s:o}", - "refunded", god->refunded, - "refund_pending", god->refund_available, - "refund_amount", TALER_JSON_from_amount (&god->refund_amount)); + json_t *context; + + context = json_pack ("{s:O, s:s, s:s}", + "contract_terms", + god->contract_terms, + "order_summary", + get_order_summary (god), + "refund_amount", + TALER_amount2s (&god->refund_amount)); + GNUNET_assert (NULL != context); + res = TMH_return_from_template (god->sc.con, + MHD_HTTP_OK, + "show_order_details", + hc->instance->settings.id, + NULL, + context); + json_decref (context); + } + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + return MHD_NO; } + return MHD_YES; } + return TALER_MHD_reply_json_pack ( + connection, + MHD_HTTP_OK, + "{s:b, s:b, s:o}", + "refunded", god->refunded, + "refund_pending", god->refund_available, + "refund_amount", TALER_JSON_from_amount (&god->refund_amount)); } diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.h b/src/backend/taler-merchant-httpd_get-orders-ID.h index b75e794f..67dd2a1a 100644 --- a/src/backend/taler-merchant-httpd_get-orders-ID.h +++ b/src/backend/taler-merchant-httpd_get-orders-ID.h @@ -24,6 +24,13 @@ #include "taler-merchant-httpd.h" +/** + * Force resuming all suspended order lookups, needed during shutdown. + */ +void +TMH_force_wallet_get_order_resume (void); + + /** * Create a taler://pay/ URI for the given @a con and @a order_id * and @a session_id and @a instance_id. diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c index ed276b42..bf5e2f65 100644 --- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c @@ -1045,6 +1045,17 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, hc->instance->settings.id, &claim_token, NULL); + if ( (NULL == taler_pay_uri) || + (NULL == order_status_url) ) + { + GNUNET_break_op (0); + GNUNET_free (taler_pay_uri); + GNUNET_free (order_status_url); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, + "host"); + } ret = TALER_MHD_reply_json_pack (connection, MHD_HTTP_OK, "{s:s, s:s, s:s, s:s, s:s" -- cgit v1.2.3