diff options
Diffstat (limited to 'src/lib')
56 files changed, 4433 insertions, 4986 deletions
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 36e249ed..1e7430d4 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -10,29 +10,31 @@ lib_LTLIBRARIES = \ libtalermerchant.la libtalermerchant_la_LDFLAGS = \ - -version-info 3:0:0 \ + -version-info 5:2:0 \ -no-undefined libtalermerchant_la_SOURCES = \ merchant_api_curl_defaults.c merchant_api_curl_defaults.h \ merchant_api_common.c merchant_api_common.h \ + merchant_api_delete_account.c \ merchant_api_delete_instance.c \ merchant_api_delete_order.c \ + merchant_api_delete_otp_device.c \ merchant_api_delete_product.c \ - merchant_api_delete_reserve.c \ - merchant_api_delete_transfer.c \ merchant_api_delete_template.c \ + merchant_api_delete_transfer.c \ merchant_api_delete_webhook.c \ + merchant_api_get_account.c \ + merchant_api_get_accounts.c \ merchant_api_get_config.c \ merchant_api_get_instance.c \ merchant_api_get_instances.c \ merchant_api_get_kyc.c \ merchant_api_get_orders.c \ + merchant_api_get_otp_device.c \ + merchant_api_get_otp_devices.c \ merchant_api_get_product.c \ merchant_api_get_products.c \ - merchant_api_get_reserve.c \ - merchant_api_get_reserves.c \ - merchant_api_get_tips.c \ merchant_api_get_transfers.c \ merchant_api_get_template.c \ merchant_api_get_templates.c \ @@ -40,12 +42,14 @@ libtalermerchant_la_SOURCES = \ merchant_api_get_webhooks.c \ merchant_api_lock_product.c \ merchant_api_merchant_get_order.c \ - merchant_api_merchant_get_tip.c \ + merchant_api_patch_account.c \ merchant_api_patch_instance.c \ merchant_api_patch_order_forget.c \ + merchant_api_patch_otp_device.c \ merchant_api_patch_product.c \ merchant_api_patch_template.c \ merchant_api_patch_webhook.c \ + merchant_api_post_account.c \ merchant_api_post_instance_auth.c \ merchant_api_post_instances.c \ merchant_api_post_orders.c \ @@ -54,23 +58,22 @@ libtalermerchant_la_SOURCES = \ merchant_api_post_order_paid.c \ merchant_api_post_order_pay.c \ merchant_api_post_order_refund.c \ + merchant_api_post_otp_devices.c \ merchant_api_post_products.c \ - merchant_api_post_reserves.c \ merchant_api_post_transfers.c \ merchant_api_post_templates.c \ + merchant_api_post_tokenfamilies.c \ merchant_api_post_using_templates.c \ merchant_api_post_webhooks.c \ - merchant_api_tip_authorize.c \ - merchant_api_tip_pickup.c \ - merchant_api_tip_pickup2.c \ - merchant_api_wallet_get_tip.c \ merchant_api_wallet_get_order.c \ + merchant_api_wallet_get_template.c \ merchant_api_wallet_post_order_refund.c libtalermerchant_la_LIBADD = \ -ltalerexchange \ -ltalercurl \ -ltalerjson \ + -ltalerkyclogic \ -ltalerutil \ -lgnunetcurl \ -lgnunetjson \ diff --git a/src/lib/merchant_api_common.c b/src/lib/merchant_api_common.c index 00e0358d..f5569ce3 100644 --- a/src/lib/merchant_api_common.c +++ b/src/lib/merchant_api_common.c @@ -245,7 +245,7 @@ TALER_MERCHANT_parse_pay_uri_free ( } -int +enum GNUNET_GenericReturnValue TALER_MERCHANT_parse_refund_uri ( const char *refund_uri, struct TALER_MERCHANT_RefundUriData *parse_data) diff --git a/src/lib/merchant_api_curl_defaults.c b/src/lib/merchant_api_curl_defaults.c index 34e4aad8..f3c4ee18 100644 --- a/src/lib/merchant_api_curl_defaults.c +++ b/src/lib/merchant_api_curl_defaults.c @@ -19,7 +19,8 @@ * @brief curl easy handle defaults * @author Florian Dold */ - +#include "platform.h" +#include <taler/taler_curl_lib.h> #include "merchant_api_curl_defaults.h" @@ -38,22 +39,14 @@ TALER_MERCHANT_curl_easy_get_ (const char *url) curl_easy_setopt (eh, CURLOPT_URL, url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_FOLLOWLOCATION, - 1L)); + TALER_curl_set_secure_redirect_policy (eh, + url); /* Enable compression (using whatever curl likes), see https://curl.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html */ GNUNET_break (CURLE_OK == curl_easy_setopt (eh, CURLOPT_ACCEPT_ENCODING, "")); - /* limit MAXREDIRS to 5 as a simple security measure against - a potential infinite loop caused by a malicious target */ - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_MAXREDIRS, - 5L)); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_TCP_FASTOPEN, diff --git a/src/lib/merchant_api_delete_account.c b/src/lib/merchant_api_delete_account.c new file mode 100644 index 00000000..42d8bc5d --- /dev/null +++ b/src/lib/merchant_api_delete_account.c @@ -0,0 +1,185 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete_account.c + * @brief Implementation of the DELETE /private/account/$H_WIRE request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP DELETE /accounts/$H_WIRE request. + * + * @param cls the `struct TALER_MERCHANT_AccountDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountDeleteHandle *adh = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountDeleteResponse adr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + adh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /accounts/$H_WIRE response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + break; + default: + /* unexpected response code */ + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for DELETE /account/ID\n", + (unsigned int) response_code, + (int) adr.hr.ec); + break; + } + adh->cb (adh->cb_cls, + &adr); + TALER_MERCHANT_account_delete_cancel (adh); +} + + +struct TALER_MERCHANT_AccountDeleteHandle * +TALER_MERCHANT_account_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MerchantWireHashP *h_wire, + TALER_MERCHANT_AccountDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountDeleteHandle *adh; + + adh = GNUNET_new (struct TALER_MERCHANT_AccountDeleteHandle); + adh->ctx = ctx; + adh->cb = cb; + adh->cb_cls = cb_cls; + { + char h_wire_str[sizeof (*h_wire) * 2]; + char *path; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_wire, + sizeof (*h_wire), + h_wire_str, + sizeof (h_wire_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "private/account/%s", + h_wire_str); + adh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == adh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (adh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + adh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (adh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + adh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_account_finished, + adh); + } + return adh; +} + + +void +TALER_MERCHANT_account_delete_cancel ( + struct TALER_MERCHANT_AccountDeleteHandle *adh) +{ + if (NULL != adh->job) + GNUNET_CURL_job_cancel (adh->job); + GNUNET_free (adh->url); + GNUNET_free (adh); +} diff --git a/src/lib/merchant_api_delete_otp_device.c b/src/lib/merchant_api_delete_otp_device.c new file mode 100644 index 00000000..5397606c --- /dev/null +++ b/src/lib/merchant_api_delete_otp_device.c @@ -0,0 +1,184 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete_otp_device.c + * @brief Implementation of the DELETE /otp-devices/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDeviceDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDeviceDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDeviceDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tdh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got DELETE /otp-devices/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + tdh->cb (tdh->cb_cls, + &hr); + TALER_MERCHANT_otp_device_delete_cancel (tdh); +} + + +struct TALER_MERCHANT_OtpDeviceDeleteHandle * +TALER_MERCHANT_otp_device_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + TALER_MERCHANT_OtpDeviceDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh; + + tdh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceDeleteHandle); + tdh->ctx = ctx; + tdh->cb = cb; + tdh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tdh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tdh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tdh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tdh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + tdh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_otp_device_finished, + tdh); + } + return tdh; +} + + +void +TALER_MERCHANT_otp_device_delete_cancel ( + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh) +{ + if (NULL != tdh->job) + GNUNET_CURL_job_cancel (tdh->job); + GNUNET_free (tdh->url); + GNUNET_free (tdh); +} diff --git a/src/lib/merchant_api_delete_reserve.c b/src/lib/merchant_api_delete_reserve.c deleted file mode 100644 index 8062d040..00000000 --- a/src/lib/merchant_api_delete_reserve.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_reserve.c - * @brief Implementation of the DELETE /reserves/$RESERVE_PUB request of the merchant's HTTP API - * @author Jonathan Buchanan - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /reserves/$RESERVE_PUB operation. - */ -struct TALER_MERCHANT_ReserveDeleteHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ReserveDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP DELETE /reserves/$RESERVE_PUB request. - * - * @param cls the `struct TALER_MERCHANT_InstanceDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_reserve_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ReserveDeleteHandle *rdh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - rdh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /reserves/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - rdh->cb (rdh->cb_cls, - &hr); - TALER_MERCHANT_reserve_delete_cancel (rdh); -} - - -/** - * Delete the private key of a reserve. - * - * @param ctx the context - * @param backend_url HTTP base URL for the backend - * @param reserve_pub which reserve should be deleted - * @param purge purge instead of just deleting - * @param cb function to call with the backend's return - * @param cb_cls closure for @a config_cb - * @return the instances handle; NULL upon error - */ -static struct TALER_MERCHANT_ReserveDeleteHandle * -reserve_delete (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_ReservePublicKeyP *reserve_pub, - bool purge, - TALER_MERCHANT_ReserveDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ReserveDeleteHandle *rdh; - - rdh = GNUNET_new (struct TALER_MERCHANT_ReserveDeleteHandle); - rdh->ctx = ctx; - rdh->cb = cb; - rdh->cb_cls = cb_cls; - { - char res_str[sizeof (*reserve_pub) * 2]; - char arg_str[sizeof (res_str) + 32]; - char *end; - - end = GNUNET_STRINGS_data_to_string (reserve_pub, - sizeof (*reserve_pub), - res_str, - sizeof (res_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "private/reserves/%s", - res_str); - if (purge) - rdh->url = TALER_url_join (backend_url, - arg_str, - "purge", - "yes", - NULL); - else - rdh->url = TALER_url_join (backend_url, - arg_str, - NULL); - } - if (NULL == rdh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (rdh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - rdh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (rdh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - rdh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_reserve_finished, - rdh); - } - return rdh; -} - - -struct TALER_MERCHANT_ReserveDeleteHandle * -TALER_MERCHANT_reserve_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_ReservePublicKeyP *reserve_pub, - TALER_MERCHANT_ReserveDeleteCallback cb, - void *cb_cls) -{ - return reserve_delete (ctx, - backend_url, - reserve_pub, - false, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_ReserveDeleteHandle * -TALER_MERCHANT_reserve_purge (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_ReservePublicKeyP *reserve_pub, - TALER_MERCHANT_ReserveDeleteCallback cb, - void *cb_cls) -{ - return reserve_delete (ctx, - backend_url, - reserve_pub, - true, - cb, - cb_cls); -} - - -void -TALER_MERCHANT_reserve_delete_cancel ( - struct TALER_MERCHANT_ReserveDeleteHandle *rdh) -{ - if (NULL != rdh->job) - GNUNET_CURL_job_cancel (rdh->job); - GNUNET_free (rdh->url); - GNUNET_free (rdh); -} diff --git a/src/lib/merchant_api_delete_template.c b/src/lib/merchant_api_delete_template.c index 0a5924aa..b2083cc9 100644 --- a/src/lib/merchant_api_delete_template.c +++ b/src/lib/merchant_api_delete_template.c @@ -74,8 +74,8 @@ struct TALER_MERCHANT_TemplateDeleteHandle */ static void handle_delete_template_finished (void *cls, - long response_code, - const void *response) + long response_code, + const void *response) { struct TALER_MERCHANT_TemplateDeleteHandle *tdh = cls; const json_t *json = response; diff --git a/src/lib/merchant_api_get_account.c b/src/lib/merchant_api_get_account.c new file mode 100644 index 00000000..84595e80 --- /dev/null +++ b/src/lib/merchant_api_get_account.c @@ -0,0 +1,211 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_account.c + * @brief Implementation of the GET /accounts/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /accounts/$ID request. + * + * @param cls the `struct TALER_MERCHANT_AccountGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /accounts/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("salt", + &tgr.details.ok.ad.salt), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_web_url ("credit_facade_url", + &tgr.details.ok.ad.credit_facade_url), + NULL), + TALER_JSON_spec_payto_uri ("payto_uri", + &tgr.details.ok.ad.payto_uri), + GNUNET_JSON_spec_fixed_auto ("h_wire", + &tgr.details.ok.ad.h_wire), + GNUNET_JSON_spec_bool ("active", + &tgr.details.ok.ad.active), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_account_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_account_get_cancel (tgh); +} + + +struct TALER_MERCHANT_AccountGetHandle * +TALER_MERCHANT_account_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const struct TALER_MerchantWireHashP *h_wire, + TALER_MERCHANT_AccountGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_AccountGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char w_str[sizeof (*h_wire) * 2]; + char *path; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_wire, + sizeof (*h_wire), + w_str, + sizeof (w_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "private/accounts/%s", + w_str); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_account_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_account_get_cancel ( + struct TALER_MERCHANT_AccountGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_accounts.c b/src/lib/merchant_api_get_accounts.c new file mode 100644 index 00000000..c08cd92d --- /dev/null +++ b/src/lib/merchant_api_get_accounts.c @@ -0,0 +1,247 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_accounts.c + * @brief Implementation of the GET /accounts request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Maximum number of accounts permitted. + */ +#define MAX_ACCOUNTS 1024 + +/** + * Handle for a GET /accounts operation. + */ +struct TALER_MERCHANT_AccountsGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountsGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse account information from @a ia. + * + * @param ia JSON array (or NULL!) with account data + * @param[in] tgr partially filled response + * @param tgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_accounts (const json_t *ia, + struct TALER_MERCHANT_AccountsGetResponse *tgr, + struct TALER_MERCHANT_AccountsGetHandle *tgh) +{ + unsigned int tmpl_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) tmpl_len) || + (tmpl_len > MAX_ACCOUNTS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_AccountEntry tmpl[GNUNET_NZL (tmpl_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_AccountEntry *ie = &tmpl[index]; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_payto_uri ("payto_uri", + &ie->payto_uri), + GNUNET_JSON_spec_fixed_auto ("h_wire", + &ie->h_wire), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.accounts_length = tmpl_len; + tgr->details.ok.accounts = tmpl; + tgh->cb (tgh->cb_cls, + tgr); + tgh->cb = NULL; /* just to be sure */ + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /accounts request. + * + * @param cls the `struct TALER_MERCHANT_AccountsGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_accounts_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountsGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountsGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /accounts response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *accounts; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("accounts", + &accounts), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_accounts (accounts, + &tgr, + tgh)) + { + TALER_MERCHANT_accounts_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_accounts_get_cancel (tgh); +} + + +struct TALER_MERCHANT_AccountsGetHandle * +TALER_MERCHANT_accounts_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_AccountsGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountsGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_AccountsGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + tgh->url = TALER_url_join (backend_url, + "private/accounts", + NULL); + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_accounts_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_accounts_get_cancel ( + struct TALER_MERCHANT_AccountsGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_config.c b/src/lib/merchant_api_get_config.c index 088434c5..b4b700bd 100644 --- a/src/lib/merchant_api_get_config.c +++ b/src/lib/merchant_api_get_config.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018, 2020 Taler Systems SA + Copyright (C) 2014-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -34,13 +34,22 @@ * Which version of the Taler protocol is implemented * by this library? Used to determine compatibility. */ -#define MERCHANT_PROTOCOL_CURRENT 3 +#define MERCHANT_PROTOCOL_CURRENT 14 /** - * How many configs are we backwards compatible with? + * How many configs are we backwards-compatible with? */ -#define MERCHANT_PROTOCOL_AGE 1 +#define MERCHANT_PROTOCOL_AGE 2 +/** + * How many exchanges do we allow at most per merchant? + */ +#define MAX_EXCHANGES 1024 + +/** + * How many currency specs do we allow at most per merchant? + */ +#define MAX_CURRENCIES 1024 /** * @brief A handle for /config operations @@ -90,9 +99,9 @@ handle_config_finished (void *cls, { struct TALER_MERCHANT_ConfigGetHandle *vgh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_ConfigResponse cr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -104,78 +113,162 @@ handle_config_finished (void *cls, { case MHD_HTTP_OK: { - struct TALER_MERCHANT_ConfigInformation vi; - enum TALER_MERCHANT_VersionCompatibility vc = - TALER_MERCHANT_VC_PROTOCOL_ERROR; + const json_t *jcs; + const json_t *exchanges = NULL; + struct TALER_MERCHANT_ExchangeConfigInfo *eci = NULL; + unsigned int num_eci = 0; + unsigned int nspec; + struct TALER_JSON_ProtocolVersion pv; struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_object_const ("currencies", + &jcs), + /* Optional for v5 compatibility, remove + once we reduce age! */ + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("exchanges", + &exchanges), + NULL), GNUNET_JSON_spec_string ("currency", - &vi.currency), + &cr.details.ok.ci.currency), + TALER_JSON_spec_version ("version", + &pv), GNUNET_JSON_spec_string ("version", - &vi.version), + &cr.details.ok.ci.version), GNUNET_JSON_spec_end () }; + cr.details.ok.compat = TALER_MERCHANT_VC_PROTOCOL_ERROR; if (GNUNET_OK != GNUNET_JSON_parse (json, spec, NULL, NULL)) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - else + cr.details.ok.compat = TALER_MERCHANT_VC_MATCH; + if (MERCHANT_PROTOCOL_CURRENT < pv.current) + { + cr.details.ok.compat |= TALER_MERCHANT_VC_NEWER; + if (MERCHANT_PROTOCOL_CURRENT < pv.current - pv.age) + cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE; + } + if (MERCHANT_PROTOCOL_CURRENT > pv.current) + { + cr.details.ok.compat |= TALER_MERCHANT_VC_OLDER; + if (MERCHANT_PROTOCOL_CURRENT - MERCHANT_PROTOCOL_AGE > pv.current) + cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE; + } + + nspec = (unsigned int) json_object_size (jcs); + if ( (nspec > MAX_CURRENCIES) || + (json_object_size (jcs) != (size_t) nspec) ) { - unsigned int age; - unsigned int revision; - unsigned int current; - - if (3 != sscanf (vi.version, - "%u:%u:%u", - ¤t, - &revision, - &age)) + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (NULL != exchanges) + { + num_eci = (unsigned int) json_object_size (exchanges); + if ( (num_eci > MAX_EXCHANGES) || + (json_object_size (exchanges) != (size_t) num_eci) ) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - else + eci = GNUNET_new_array (num_eci, + struct TALER_MERCHANT_ExchangeConfigInfo); + for (unsigned int i = 0; i<num_eci; i++) { - vc = TALER_MERCHANT_VC_MATCH; - if (MERCHANT_PROTOCOL_CURRENT < current) + struct TALER_MERCHANT_ExchangeConfigInfo *ei = &eci[i]; + const json_t *ej = json_array_get (exchanges, + i); + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_string ("currency", + &ei->currency), + GNUNET_JSON_spec_string ("base_url", + &ei->base_url), + GNUNET_JSON_spec_fixed_auto ("master_pub", + &ei->master_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (ej, + ispec, + NULL, NULL)) { - vc |= TALER_MERCHANT_VC_NEWER; - if (MERCHANT_PROTOCOL_CURRENT < current - age) - vc |= TALER_MERCHANT_VC_INCOMPATIBLE; + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_free (eci); + break; } - if (MERCHANT_PROTOCOL_CURRENT > current) + } + } + { + struct TALER_CurrencySpecification *cspecs; + unsigned int off = 0; + json_t *obj; + const char *curr; + + cspecs = GNUNET_new_array (nspec, + struct TALER_CurrencySpecification); + cr.details.ok.num_cspecs = nspec; + cr.details.ok.cspecs = cspecs; + cr.details.ok.num_exchanges = (unsigned int) num_eci; + cr.details.ok.exchanges = eci; + json_object_foreach ((json_t *) jcs, curr, obj) + { + struct TALER_CurrencySpecification *cs = &cspecs[off++]; + struct GNUNET_JSON_Specification cspec[] = { + TALER_JSON_spec_currency_specification (curr, + curr, + cs), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (jcs, + cspec, + NULL, NULL)) { - vc |= TALER_MERCHANT_VC_OLDER; - if (MERCHANT_PROTOCOL_CURRENT - MERCHANT_PROTOCOL_AGE > current) - vc |= TALER_MERCHANT_VC_INCOMPATIBLE; + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_free (eci); + TALER_CONFIG_free_currencies (off - 1, + cspecs); + break; } } + vgh->cb (vgh->cb_cls, + &cr); + GNUNET_free (eci); + TALER_CONFIG_free_currencies (nspec, + cspecs); } - vgh->cb (vgh->cb_cls, - &hr, - &vi, - vc); TALER_MERCHANT_config_get_cancel (vgh); return; } default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + cr.hr.ec = TALER_JSON_get_error_code (json); + cr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); - vgh->cb (vgh->cb_cls, - &hr, - NULL, - TALER_MERCHANT_VC_PROTOCOL_ERROR); + (int) cr.hr.ec); break; } + vgh->cb (vgh->cb_cls, + &cr); TALER_MERCHANT_config_get_cancel (vgh); } diff --git a/src/lib/merchant_api_get_instance.c b/src/lib/merchant_api_get_instance.c index bb71a1ed..eef95b84 100644 --- a/src/lib/merchant_api_get_instance.c +++ b/src/lib/merchant_api_get_instance.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018, 2020 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -28,6 +28,7 @@ #include "taler_merchant_service.h" #include "merchant_api_curl_defaults.h" #include <taler/taler_json_lib.h> +#include <taler/taler_kyclogic_lib.h> #include <taler/taler_signatures.h> @@ -79,9 +80,9 @@ handle_get_instance_finished (void *cls, { struct TALER_MERCHANT_InstanceGetHandle *igh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_InstanceGetResponse igr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; igh->job = NULL; @@ -92,135 +93,86 @@ handle_get_instance_finished (void *cls, { case MHD_HTTP_OK: { - json_t *accounts; - const char *name; - struct TALER_MerchantPublicKeyP merchant_pub; - json_t *address; - json_t *jurisdiction; - struct TALER_Amount default_max_wire_fee; - uint32_t default_wire_fee_amortization; - struct TALER_Amount default_max_deposit_fee; - struct GNUNET_TIME_Relative default_wire_transfer_delay; - struct GNUNET_TIME_Relative default_pay_delay; + const char *uts; + const json_t *address; + const json_t *jurisdiction; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("accounts", - &accounts), - GNUNET_JSON_spec_string ("name", - &name), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), - GNUNET_JSON_spec_json ("address", - &address), - GNUNET_JSON_spec_json ("jurisdiction", - &jurisdiction), - TALER_JSON_spec_amount_any ("default_max_wire_fee", - &default_max_wire_fee), - GNUNET_JSON_spec_uint32 ("default_wire_fee_amortization", - &default_wire_fee_amortization), - TALER_JSON_spec_amount_any ("default_max_deposit_fee", - &default_max_deposit_fee), - GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay", - &default_wire_transfer_delay), - GNUNET_JSON_spec_relative_time ("default_pay_delay", - &default_pay_delay), + GNUNET_JSON_spec_string ( + "name", + &igr.details.ok.details.name), + GNUNET_JSON_spec_string ( + "user_type", + &uts), + GNUNET_JSON_spec_fixed_auto ( + "merchant_pub", + &igr.details.ok.details.merchant_pub), + GNUNET_JSON_spec_object_const ( + "address", + &address), + GNUNET_JSON_spec_object_const ( + "jurisdiction", + &jurisdiction), + GNUNET_JSON_spec_bool ( + "use_stefan", + &igr.details.ok.details.use_stefan), + GNUNET_JSON_spec_relative_time ( + "default_wire_transfer_delay", + &igr.details.ok.details.default_wire_transfer_delay), + GNUNET_JSON_spec_relative_time ( + "default_pay_delay", + &igr.details.ok.details.default_pay_delay), GNUNET_JSON_spec_end () }; - if ( (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) && - (json_is_array (accounts)) ) + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) { - unsigned int accounts_length = json_array_size (accounts); - struct TALER_MERCHANT_Account aa[accounts_length]; - const char *payto_uris[accounts_length]; - size_t index; - json_t *value; - int ret = GNUNET_OK; - - memset (payto_uris, 0, sizeof (payto_uris)); - json_array_foreach (accounts, index, value) - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("salt", - &aa[index].salt), - GNUNET_JSON_spec_string ("payto_uri", - &payto_uris[index]), - GNUNET_JSON_spec_fixed_auto ("h_wire", - &aa[index].h_wire), - GNUNET_JSON_spec_bool ("active", - &aa[index].active), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - break; - } - aa[index].payto_uri = payto_uris[index]; - } - - if (GNUNET_OK == ret) - { - struct TALER_MERCHANT_InstanceDetails details = { - .name = name, - .merchant_pub = &merchant_pub, - .address = address, - .jurisdiction = jurisdiction, - .default_max_wire_fee = &default_max_wire_fee, - .default_wire_fee_amortization = default_wire_fee_amortization, - .default_max_deposit_fee = &default_max_deposit_fee, - .default_wire_transfer_delay = default_wire_transfer_delay, - .default_pay_delay = default_pay_delay - }; - - igh->cb (igh->cb_cls, - &hr, - accounts_length, - aa, - &details); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_instance_get_cancel (igh); - return; - } + GNUNET_break_op (0); + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + igr.details.ok.details.address = address; + igr.details.ok.details.jurisdiction = jurisdiction; + if (GNUNET_OK != + TALER_KYCLOGIC_kyc_user_type_from_string ( + uts, + &igr.details.ok.details.ut)) + { + GNUNET_break_op (0); + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_JSON_parse_free (spec); - break; + igh->cb (igh->cb_cls, + &igr); + TALER_MERCHANT_instance_get_cancel (igh); + return; } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; case MHD_HTTP_NOT_FOUND: /* instance does not exist */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) igr.hr.ec); break; } igh->cb (igh->cb_cls, - &hr, - 0, - NULL, - NULL); + &igr); TALER_MERCHANT_instance_get_cancel (igh); } diff --git a/src/lib/merchant_api_get_instances.c b/src/lib/merchant_api_get_instances.c index 52a462b9..b2f99853 100644 --- a/src/lib/merchant_api_get_instances.c +++ b/src/lib/merchant_api_get_instances.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018, 2020 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -32,6 +32,11 @@ /** + * Maximum number of instances permitted. + */ +#define MAX_INSTANCES 1024 + +/** * Handle for a GET /instances operation. */ struct TALER_MERCHANT_InstancesGetHandle @@ -67,79 +72,82 @@ struct TALER_MERCHANT_InstancesGetHandle /** * Parse instance information from @a ia. * + * @param json overall reply body * @param ia JSON array (or NULL!) with instance data * @param igh operation handle * @return #GNUNET_OK on success */ -static int -parse_instances (const json_t *ia, +static enum GNUNET_GenericReturnValue +parse_instances (const json_t *json, + const json_t *ia, struct TALER_MERCHANT_InstancesGetHandle *igh) { - unsigned int iis_len = json_array_size (ia); - struct TALER_MERCHANT_InstanceInformation iis[iis_len]; - size_t index; - json_t *value; - int ret; + unsigned int iis_len = (unsigned int) json_array_size (ia); - ret = GNUNET_OK; - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_InstanceInformation *ii = &iis[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("name", - &ii->name), - GNUNET_JSON_spec_string ("id", - &ii->id), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &ii->merchant_pub), - GNUNET_JSON_spec_json ("payment_targets", - &ii->payment_targets), - GNUNET_JSON_spec_end () + if ( (json_array_size (ia) != (size_t) iis_len) || + (iis_len > MAX_INSTANCES) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_InstanceInformation iis[GNUNET_NZL (iis_len)]; + size_t index; + json_t *value; + struct TALER_MERCHANT_InstancesGetResponse igr = { + .hr.http_status = MHD_HTTP_OK, + .hr.reply = json, + .details.ok.iis_length = iis_len, + .details.ok.iis = iis }; - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (! json_is_array (ii->payment_targets)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - break; - } - for (unsigned int i = 0; i<json_array_size (ii->payment_targets); i++) - { - if (! json_is_string (json_array_get (ii->payment_targets, - i))) + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_InstanceInformation *ii = &iis[index]; + const char *uts; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("name", + &ii->name), + GNUNET_JSON_spec_string ("user_type", + &uts), + GNUNET_JSON_spec_string ("id", + &ii->id), + GNUNET_JSON_spec_fixed_auto ("merchant_pub", + &ii->merchant_pub), + GNUNET_JSON_spec_array_const ("payment_targets", + &ii->payment_targets), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) { GNUNET_break_op (0); - ret = GNUNET_SYSERR; - break; + return GNUNET_SYSERR; } - } - if (GNUNET_SYSERR == ret) - break; - } - if (GNUNET_OK == ret) - { - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK - }; - + if (GNUNET_OK != + TALER_KYCLOGIC_kyc_user_type_from_string (uts, + &ii->ut)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (size_t i = 0; i<json_array_size (ii->payment_targets); i++) + { + if (! json_is_string (json_array_get (ii->payment_targets, + i))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + } /* for all instances */ igh->cb (igh->cb_cls, - &hr, - iis_len, - iis); + &igr); igh->cb = NULL; /* just to be sure */ } - for (unsigned int i = 0; i<iis_len; i++) - if (NULL != iis[i].payment_targets) - json_decref (iis[i].payment_targets); - return ret; + return GNUNET_OK; } @@ -158,9 +166,9 @@ handle_instances_finished (void *cls, { struct TALER_MERCHANT_InstancesGetHandle *igh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_InstancesGetResponse igr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; igh->job = NULL; @@ -171,10 +179,10 @@ handle_instances_finished (void *cls, { case MHD_HTTP_OK: { - json_t *instances; + const json_t *instances; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("instances", - &instances), + GNUNET_JSON_spec_array_const ("instances", + &instances), GNUNET_JSON_spec_end () }; @@ -183,47 +191,38 @@ handle_instances_finished (void *cls, spec, NULL, NULL)) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - else + if (GNUNET_OK == + parse_instances (json, + instances, + igh)) { - if ( (! json_is_array (instances)) || - (GNUNET_OK == - parse_instances (instances, - igh)) ) - { - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_instances_get_cancel (igh); - return; - } - else - { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } + TALER_MERCHANT_instances_get_cancel (igh); + return; } - GNUNET_JSON_parse_free (spec); + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) igr.hr.ec); break; } igh->cb (igh->cb_cls, - &hr, - 0, - NULL); + &igr); TALER_MERCHANT_instances_get_cancel (igh); } diff --git a/src/lib/merchant_api_get_kyc.c b/src/lib/merchant_api_get_kyc.c index bc688eed..d2a819ea 100644 --- a/src/lib/merchant_api_get_kyc.c +++ b/src/lib/merchant_api_get_kyc.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021 Taler Systems SA + Copyright (C) 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -32,6 +32,11 @@ /** + * Maximum length of the KYC arrays supported. + */ +#define MAX_KYC 1024 + +/** * Handle for a GET /kyc operation. */ struct TALER_MERCHANT_KycGetHandle @@ -76,70 +81,91 @@ struct TALER_MERCHANT_KycGetHandle static enum GNUNET_GenericReturnValue parse_kyc (struct TALER_MERCHANT_KycGetHandle *kyc, struct TALER_MERCHANT_KycResponse *kr, - json_t *pends, - json_t *touts) + const json_t *pends, + const json_t *touts) { - unsigned int num_pends = json_array_size (pends); - unsigned int num_touts = json_array_size (touts); - struct TALER_MERCHANT_AccountKycRedirectDetail pending_kycs[GNUNET_NZL ( - num_pends)]; - struct TALER_MERCHANT_ExchangeKycFailureDetail timeout_kycs[GNUNET_NZL ( - num_touts)]; - - for (unsigned int i = 0; i<num_pends; i++) + unsigned int num_pends = (unsigned int) json_array_size (pends); + unsigned int num_touts = (unsigned int) json_array_size (touts); + + if ( (json_array_size (pends) != (size_t) num_pends) || + (num_pends > MAX_KYC) ) { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("kyc_url", - &pending_kycs[i].kyc_url), - GNUNET_JSON_spec_string ("exchange_url", - &pending_kycs[i].exchange_url), - GNUNET_JSON_spec_string ("payto_uri", - &pending_kycs[i].payto_uri), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json_array_get (pends, - i), - spec, - NULL, NULL)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + GNUNET_break (0); + return GNUNET_SYSERR; + } + if ( (json_array_size (touts) != (size_t) num_touts) || + (num_touts > MAX_KYC) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; } - for (unsigned int i = 0; i<num_touts; i++) + { - uint32_t hs; - uint32_t ec; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("exchange_url", - &timeout_kycs[i].exchange_url), - GNUNET_JSON_spec_uint32 ("exchange_code", - &ec), - GNUNET_JSON_spec_uint32 ("exchange_http_status", - &hs), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json_array_get (touts, - i), - spec, - NULL, NULL)) + struct TALER_MERCHANT_AccountKycRedirectDetail pending_kycs[ + GNUNET_NZL (num_pends)]; + struct TALER_MERCHANT_ExchangeKycFailureDetail timeout_kycs[ + GNUNET_NZL (num_touts)]; + + memset (pending_kycs, + 0, + sizeof (pending_kycs)); + for (unsigned int i = 0; i<num_pends; i++) + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_web_url ("kyc_url", + &pending_kycs[i].kyc_url), + NULL), + TALER_JSON_spec_aml_decision ("aml_status", + &pending_kycs[i].aml_status), + TALER_JSON_spec_web_url ("exchange_url", + &pending_kycs[i].exchange_url), + TALER_JSON_spec_payto_uri ("payto_uri", + &pending_kycs[i].payto_uri), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json_array_get (pends, + i), + spec, + NULL, NULL)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + for (unsigned int i = 0; i<num_touts; i++) { - GNUNET_break (0); - return GNUNET_SYSERR; + uint32_t hs; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_web_url ("exchange_url", + &timeout_kycs[i].exchange_url), + TALER_JSON_spec_ec ("exchange_code", + &timeout_kycs[i].exchange_code), + GNUNET_JSON_spec_uint32 ("exchange_http_status", + &hs), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json_array_get (touts, + i), + spec, + NULL, NULL)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + timeout_kycs[i].exchange_http_status = (unsigned int) hs; } - timeout_kycs[i].exchange_http_status = (unsigned int) hs; - timeout_kycs[i].exchange_code = (enum TALER_ErrorCode) ec; + kr->details.kyc_status.pending_kycs = pending_kycs; + kr->details.kyc_status.timeout_kycs = timeout_kycs; + kr->details.kyc_status.pending_kycs_length = num_pends; + kr->details.kyc_status.timeout_kycs_length = num_touts; + kyc->cb (kyc->cb_cls, + kr); } - kr->details.kyc_status.pending_kycs = pending_kycs; - kr->details.kyc_status.timeout_kycs = timeout_kycs; - kr->details.kyc_status.pending_kycs_length = num_pends; - kr->details.kyc_status.timeout_kycs_length = num_touts; - kyc->cb (kyc->cb_cls, - kr); return GNUNET_OK; } @@ -176,13 +202,13 @@ handle_get_kyc_finished (void *cls, case MHD_HTTP_BAD_GATEWAY: case MHD_HTTP_GATEWAY_TIMEOUT: { - json_t *pends; - json_t *touts; + const json_t *pends; + const json_t *touts; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("pending_kycs", - &pends), - GNUNET_JSON_spec_json ("timeout_kycs", - &touts), + GNUNET_JSON_spec_array_const ("pending_kycs", + &pends), + GNUNET_JSON_spec_array_const ("timeout_kycs", + &touts), GNUNET_JSON_spec_end () }; @@ -195,20 +221,17 @@ handle_get_kyc_finished (void *cls, kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } - if ( (! json_is_array (pends)) || - (! json_is_array (touts)) || - (GNUNET_OK != - parse_kyc (kyc, - &kr, - pends, - touts)) ) + if (GNUNET_OK != + parse_kyc (kyc, + &kr, + pends, + touts)) { kr.hr.http_status = 0; kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } /* parse_kyc called the continuation already */ - GNUNET_JSON_parse_free (spec); TALER_MERCHANT_kyc_get_cancel (kyc); return; } @@ -260,17 +283,19 @@ kyc_get (struct GNUNET_CURL_Context *ctx, struct TALER_MERCHANT_KycGetHandle *kyc; CURL *eh; char timeout_ms[32]; + unsigned int tms; kyc = GNUNET_new (struct TALER_MERCHANT_KycGetHandle); kyc->ctx = ctx; kyc->cb = cb; kyc->cb_cls = cb_cls; + tms = (unsigned int) (timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS. + rel_value_us); GNUNET_snprintf (timeout_ms, sizeof (timeout_ms), - "%llu", - (unsigned long long) (timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS. - rel_value_us)); + "%u", + tms); kyc->url = TALER_url_join (url, "kyc", "h_wire", @@ -298,6 +323,13 @@ kyc_get (struct GNUNET_CURL_Context *ctx, "Requesting URL '%s'\n", kyc->url); eh = TALER_MERCHANT_curl_easy_get_ (kyc->url); + if (0 != tms) + { + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 100L))); + } kyc->job = GNUNET_CURL_job_add (ctx, eh, &handle_get_kyc_finished, @@ -321,7 +353,7 @@ TALER_MERCHANT_kyc_get (struct GNUNET_CURL_Context *ctx, "%sprivate/", backend_url); return kyc_get (ctx, - url, + url, /* consumed! */ h_wire, exchange_url, timeout, @@ -347,7 +379,7 @@ TALER_MERCHANT_management_kyc_get (struct GNUNET_CURL_Context *ctx, backend_url, instance_id); return kyc_get (ctx, - url, + url, /* consumed! */ h_wire, exchange_url, timeout, diff --git a/src/lib/merchant_api_get_orders.c b/src/lib/merchant_api_get_orders.c index 7f08acb6..459409fd 100644 --- a/src/lib/merchant_api_get_orders.c +++ b/src/lib/merchant_api_get_orders.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018, 2020, 2021 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -30,6 +30,10 @@ #include <taler/taler_json_lib.h> #include <taler/taler_signatures.h> +/** + * Maximum number of orders we return. + */ +#define MAX_ORDERS 1024 /** * Handle for a GET /orders operation. @@ -68,65 +72,64 @@ struct TALER_MERCHANT_OrdersGetHandle * Parse order information from @a ia. * * @param ia JSON array (or NULL!) with order data + * @param[in] ogr response to fill * @param ogh operation handle * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue parse_orders (const json_t *ia, + struct TALER_MERCHANT_OrdersGetResponse *ogr, struct TALER_MERCHANT_OrdersGetHandle *ogh) { - unsigned int oes_len = json_array_size (ia); - struct TALER_MERCHANT_OrderEntry oes[oes_len]; - size_t index; - json_t *value; - int ret; - - ret = GNUNET_OK; - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_OrderEntry *ie = &oes[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("order_id", - &ie->order_id), - GNUNET_JSON_spec_timestamp ("timestamp", - &ie->timestamp), - GNUNET_JSON_spec_uint64 ("row_id", - &ie->order_serial), - TALER_JSON_spec_amount_any ("amount", - &ie->amount), - GNUNET_JSON_spec_string ("summary", - &ie->summary), - GNUNET_JSON_spec_bool ("refundable", - &ie->refundable), - GNUNET_JSON_spec_bool ("paid", - &ie->paid), - GNUNET_JSON_spec_end () - }; + unsigned int oes_len = (unsigned int) json_array_size (ia); - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; + if ( (json_array_size (ia) != (size_t) oes_len) || + (oes_len > MAX_ORDERS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; } - if (GNUNET_OK == ret) { - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK - }; + struct TALER_MERCHANT_OrderEntry oes[GNUNET_NZL (oes_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_OrderEntry *ie = &oes[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("order_id", + &ie->order_id), + GNUNET_JSON_spec_timestamp ("timestamp", + &ie->timestamp), + GNUNET_JSON_spec_uint64 ("row_id", + &ie->order_serial), + TALER_JSON_spec_amount_any ("amount", + &ie->amount), + GNUNET_JSON_spec_string ("summary", + &ie->summary), + GNUNET_JSON_spec_bool ("refundable", + &ie->refundable), + GNUNET_JSON_spec_bool ("paid", + &ie->paid), + GNUNET_JSON_spec_end () + }; + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + ogr->details.ok.orders_length = oes_len; + ogr->details.ok.orders = oes; ogh->cb (ogh->cb_cls, - &hr, - oes_len, - oes); + ogr); ogh->cb = NULL; /* just to be sure */ } - return ret; + return GNUNET_OK; } @@ -145,9 +148,9 @@ handle_get_orders_finished (void *cls, { struct TALER_MERCHANT_OrdersGetHandle *ogh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_OrdersGetResponse ogr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; ogh->job = NULL; @@ -158,10 +161,10 @@ handle_get_orders_finished (void *cls, { case MHD_HTTP_OK: { - json_t *orders; + const json_t *orders; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("orders", - &orders), + GNUNET_JSON_spec_array_const ("orders", + &orders), GNUNET_JSON_spec_end () }; @@ -170,52 +173,43 @@ handle_get_orders_finished (void *cls, spec, NULL, NULL)) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + ogr.hr.http_status = 0; + ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - else + if (GNUNET_OK == + parse_orders (orders, + &ogr, + ogh)) { - if ( (! json_is_array (orders)) || - (GNUNET_OK == - parse_orders (orders, - ogh)) ) - { - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_orders_get_cancel (ogh); - return; - } - else - { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } + TALER_MERCHANT_orders_get_cancel (ogh); + return; } - GNUNET_JSON_parse_free (spec); + ogr.hr.http_status = 0; + ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ogr.hr.ec = TALER_JSON_get_error_code (json); + ogr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ogr.hr.ec = TALER_JSON_get_error_code (json); + ogr.hr.hint = TALER_JSON_get_error_hint (json); break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ogr.hr.ec = TALER_JSON_get_error_code (json); + ogr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) ogr.hr.ec); break; } ogh->cb (ogh->cb_cls, - &hr, - 0, - NULL); + &ogr); TALER_MERCHANT_orders_get_cancel (ogh); } @@ -255,12 +249,51 @@ TALER_MERCHANT_orders_get2 ( TALER_MERCHANT_OrdersGetCallback cb, void *cb_cls) { + return TALER_MERCHANT_orders_get3 ( + ctx, + backend_url, + paid, + refunded, + wired, + NULL, + NULL, + date, + start_row, + delta, + timeout, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_OrdersGetHandle * +TALER_MERCHANT_orders_get3 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + enum TALER_EXCHANGE_YesNoAll paid, + enum TALER_EXCHANGE_YesNoAll refunded, + enum TALER_EXCHANGE_YesNoAll wired, + const char *session_id, + const char *fulfillment_url, + struct GNUNET_TIME_Timestamp date, + uint64_t start_row, + int64_t delta, + struct GNUNET_TIME_Relative timeout, + TALER_MERCHANT_OrdersGetCallback cb, + void *cb_cls) +{ struct TALER_MERCHANT_OrdersGetHandle *ogh; CURL *eh; - unsigned int timeout_ms = timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; + unsigned int tms = timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; GNUNET_assert (NULL != backend_url); + if ( (delta > MAX_ORDERS) || + (delta < -MAX_ORDERS) ) + { + GNUNET_break (0); + return NULL; + } if (0 == delta) { GNUNET_break (0); @@ -274,6 +307,8 @@ TALER_MERCHANT_orders_get2 ( /* build ogh->url with the various optional arguments */ { char *dstr; + char *fec = NULL; + char *sid = NULL; bool have_date; bool have_srow; char cbuf[30]; @@ -282,8 +317,8 @@ TALER_MERCHANT_orders_get2 ( GNUNET_snprintf (tbuf, sizeof (tbuf), - "%llu", - (unsigned long long) timeout_ms); + "%u", + tms); GNUNET_snprintf (dbuf, sizeof (dbuf), "%lld", @@ -292,6 +327,14 @@ TALER_MERCHANT_orders_get2 ( sizeof (cbuf), "%llu", (unsigned long long) start_row); + if (NULL != session_id) + (void) GNUNET_STRINGS_urlencode (strlen (session_id), + session_id, + &sid); + if (NULL != fulfillment_url) + (void) GNUNET_STRINGS_urlencode (strlen (fulfillment_url), + fulfillment_url, + &fec); dstr = GNUNET_strdup (GNUNET_TIME_timestamp2s (date)); if (delta > 0) { @@ -330,11 +373,17 @@ TALER_MERCHANT_orders_get2 ( ? dbuf : NULL, "timeout_ms", - (0 != timeout_ms) + (0 != tms) ? tbuf : NULL, + "session_id", + sid, + "fulfillment_url", + fec, NULL); GNUNET_free (dstr); + GNUNET_free (sid); + GNUNET_free (fec); } if (NULL == ogh->url) { @@ -347,6 +396,20 @@ TALER_MERCHANT_orders_get2 ( "Requesting URL '%s'\n", ogh->url); eh = TALER_MERCHANT_curl_easy_get_ (ogh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (ogh->url); + GNUNET_free (ogh); + return NULL; + } + if (0 != tms) + { + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 100L))); + } ogh->job = GNUNET_CURL_job_add (ctx, eh, &handle_get_orders_finished, diff --git a/src/lib/merchant_api_get_otp_device.c b/src/lib/merchant_api_get_otp_device.c new file mode 100644 index 00000000..65950af8 --- /dev/null +++ b/src/lib/merchant_api_get_otp_device.c @@ -0,0 +1,210 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_otp_device.c + * @brief Implementation of the GET /otp-devices/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDeviceGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDeviceGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDeviceGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_OtpDeviceGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /otp-devices/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + uint32_t alg32; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("otp_device_description", + &tgr.details.ok.otp_device_description), + GNUNET_JSON_spec_uint32 ("otp_algorithm", + &alg32), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("otp_ctr", + &tgr.details.ok.otp_ctr), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("otp_timestamp", + &tgr.details.ok.otp_timestamp_s), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("otp_code", + &tgr.details.ok.otp_code), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.details.ok.otp_alg = + (enum TALER_MerchantConfirmationAlgorithm) alg32; + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_device_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_device_get_cancel (tgh); +} + + +struct TALER_MERCHANT_OtpDeviceGetHandle * +TALER_MERCHANT_otp_device_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + TALER_MERCHANT_OtpDeviceGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_otp_device_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_otp_device_get_cancel ( + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_otp_devices.c b/src/lib/merchant_api_get_otp_devices.c new file mode 100644 index 00000000..4737944c --- /dev/null +++ b/src/lib/merchant_api_get_otp_devices.c @@ -0,0 +1,248 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_otp_devices.c + * @brief Implementation of the GET /otp-devices request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Maximum number of OTP devices we return. + */ +#define MAX_OTP 1024 + + +/** + * Handle for a GET /otp-devices operation. + */ +struct TALER_MERCHANT_OtpDevicesGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicesGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse OTP device information from @a ia. + * + * @param ia JSON array (or NULL!) with otp_device data + * @param[in] tgr partially filled response + * @param tgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_otp_devices (const json_t *ia, + struct TALER_MERCHANT_OtpDevicesGetResponse *tgr, + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh) +{ + unsigned int otp_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) otp_len) || + (otp_len > MAX_OTP) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_OtpDeviceEntry otp[GNUNET_NZL (otp_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_OtpDeviceEntry *ie = &otp[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("otp_device_id", + &ie->otp_device_id), + GNUNET_JSON_spec_string ("device_description", + &ie->device_description), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.otp_devices_length = otp_len; + tgr->details.ok.otp_devices = otp; + tgh->cb (tgh->cb_cls, + tgr); + tgh->cb = NULL; /* just to be sure */ + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /otp-devices request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicesGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_otp_devices_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_OtpDevicesGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /otp-devices response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *otp_devices; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("otp_devices", + &otp_devices), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_otp_devices (otp_devices, + &tgr, + tgh)) + { + TALER_MERCHANT_otp_devices_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_devices_get_cancel (tgh); +} + + +struct TALER_MERCHANT_OtpDevicesGetHandle * +TALER_MERCHANT_otp_devices_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_OtpDevicesGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_OtpDevicesGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + tgh->url = TALER_url_join (backend_url, + "private/otp-devices", + NULL); + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_otp_devices_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_otp_devices_get_cancel ( + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_product.c b/src/lib/merchant_api_get_product.c index 94d38ec7..3f026bf3 100644 --- a/src/lib/merchant_api_get_product.c +++ b/src/lib/merchant_api_get_product.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018, 2020 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -79,9 +79,9 @@ handle_get_product_finished (void *cls, { struct TALER_MERCHANT_ProductGetHandle *pgh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_ProductGetResponse pgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; pgh->job = NULL; @@ -92,121 +92,80 @@ handle_get_product_finished (void *cls, { case MHD_HTTP_OK: { - const char *description; - json_t *description_i18n; - const char *unit; - struct TALER_Amount price; - const char *image; - json_t *taxes; - int64_t total_stock; - uint64_t total_sold; - uint64_t total_lost; - json_t *address; - bool rst_ok = true; - struct GNUNET_TIME_Timestamp next_restock - = GNUNET_TIME_UNIT_ZERO_TS; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("description", - &description), - GNUNET_JSON_spec_json ("description_i18n", - &description_i18n), - GNUNET_JSON_spec_string ("unit", - &unit), - TALER_JSON_spec_amount_any ("price", - &price), - GNUNET_JSON_spec_string ("image", - &image), - GNUNET_JSON_spec_json ("taxes", - &taxes), - GNUNET_JSON_spec_int64 ("total_stock", - &total_stock), - GNUNET_JSON_spec_uint64 ("total_sold", - &total_sold), - GNUNET_JSON_spec_uint64 ("total_lost", - &total_lost), - GNUNET_JSON_spec_json ("address", - &address), + GNUNET_JSON_spec_string ( + "description", + &pgr.details.ok.description), + GNUNET_JSON_spec_object_const ( + "description_i18n", + &pgr.details.ok.description_i18n), + GNUNET_JSON_spec_string ( + "unit", + &pgr.details.ok.unit), + TALER_JSON_spec_amount_any ( + "price", + &pgr.details.ok.price), + GNUNET_JSON_spec_string ( + "image", + &pgr.details.ok.image), + GNUNET_JSON_spec_array_const ( + "taxes", + &pgr.details.ok.taxes), + GNUNET_JSON_spec_int64 ( + "total_stock", + &pgr.details.ok.total_stock), + GNUNET_JSON_spec_uint64 ( + "total_sold", + &pgr.details.ok.total_sold), + GNUNET_JSON_spec_uint64 ( + "total_lost", + &pgr.details.ok.total_lost), + GNUNET_JSON_spec_object_const ( + "address", + &pgr.details.ok.location), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_timestamp ("next_restock", + &pgr.details.ok.next_restock), + NULL), GNUNET_JSON_spec_end () }; - if (NULL != - json_object_get (json, - "next_restock")) - { - struct GNUNET_JSON_Specification spect[] = { - GNUNET_JSON_spec_timestamp ("next_restock", - &next_restock), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spect, - NULL, NULL)) - rst_ok = false; - } - - - if ( (rst_ok) && - (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) ) + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) { pgh->cb (pgh->cb_cls, - &hr, - description, - description_i18n, - unit, - &price, - image, - taxes, - total_stock, - total_sold, - total_lost, - address, - next_restock); + &pgr); GNUNET_JSON_parse_free (spec); TALER_MERCHANT_product_get_cancel (pgh); return; } - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_JSON_parse_free (spec); + pgr.hr.http_status = 0; + pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + pgr.hr.ec = TALER_JSON_get_error_code (json); + pgr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + pgr.hr.ec = TALER_JSON_get_error_code (json); + pgr.hr.hint = TALER_JSON_get_error_hint (json); break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + pgr.hr.ec = TALER_JSON_get_error_code (json); + pgr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) pgr.hr.ec); break; } pgh->cb (pgh->cb_cls, - &hr, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - 0, - 0, - 0, - NULL, - GNUNET_TIME_UNIT_FOREVER_TS); + &pgr); TALER_MERCHANT_product_get_cancel (pgh); } diff --git a/src/lib/merchant_api_get_products.c b/src/lib/merchant_api_get_products.c index c3cc30e7..c33e24c9 100644 --- a/src/lib/merchant_api_get_products.c +++ b/src/lib/merchant_api_get_products.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018, 2020 Taler Systems SA + Copyright (C) 2014-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -32,6 +32,12 @@ /** + * Maximum number of products we return. + */ +#define MAX_PRODUCTS 1024 + + +/** * Handle for a GET /products operation. */ struct TALER_MERCHANT_ProductsGetHandle @@ -67,54 +73,68 @@ struct TALER_MERCHANT_ProductsGetHandle /** * Parse product information from @a ia. * + * @param json overall JSON reply * @param ia JSON array (or NULL!) with product data * @param pgh operation handle * @return #GNUNET_OK on success */ -static int -parse_products (const json_t *ia, +static enum GNUNET_GenericReturnValue +parse_products (const json_t *json, + const json_t *ia, struct TALER_MERCHANT_ProductsGetHandle *pgh) { unsigned int ies_len = json_array_size (ia); - struct TALER_MERCHANT_InventoryEntry ies[ies_len]; - size_t index; - json_t *value; - int ret; - - ret = GNUNET_OK; - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_InventoryEntry *ie = &ies[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("product_id", - &ie->product_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; + + if ( (json_array_size (ia) != (size_t) ies_len) || + (ies_len > MAX_PRODUCTS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; } - if (GNUNET_OK == ret) { - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK - }; - - pgh->cb (pgh->cb_cls, - &hr, - ies_len, - ies); - pgh->cb = NULL; /* just to be sure */ + struct TALER_MERCHANT_InventoryEntry ies[GNUNET_NZL (ies_len)]; + size_t index; + json_t *value; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_OK; + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_InventoryEntry *ie = &ies[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("product_id", + &ie->product_id), + GNUNET_JSON_spec_uint64 ("product_serial", + &ie->product_serial), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ret = GNUNET_SYSERR; + continue; + } + if (GNUNET_SYSERR == ret) + break; + } + if (GNUNET_OK == ret) + { + struct TALER_MERCHANT_GetProductsResponse gpr = { + .hr.http_status = MHD_HTTP_OK, + .hr.reply = json, + .details.ok.products_length = ies_len, + .details.ok.products = ies + }; + + pgh->cb (pgh->cb_cls, + &gpr); + pgh->cb = NULL; /* just to be sure */ + } + return ret; } - return ret; } @@ -133,9 +153,9 @@ handle_get_products_finished (void *cls, { struct TALER_MERCHANT_ProductsGetHandle *pgh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_GetProductsResponse gpr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; pgh->job = NULL; @@ -146,10 +166,10 @@ handle_get_products_finished (void *cls, { case MHD_HTTP_OK: { - json_t *products; + const json_t *products; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("products", - &products), + GNUNET_JSON_spec_array_const ("products", + &products), GNUNET_JSON_spec_end () }; @@ -158,48 +178,39 @@ handle_get_products_finished (void *cls, spec, NULL, NULL)) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + gpr.hr.http_status = 0; + gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - else + if (GNUNET_OK == + parse_products (json, + products, + pgh)) { - if ( (! json_is_array (products)) || - (GNUNET_OK == - parse_products (products, - pgh)) ) - { - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_products_get_cancel (pgh); - return; - } - else - { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } + TALER_MERCHANT_products_get_cancel (pgh); + return; } - GNUNET_JSON_parse_free (spec); + gpr.hr.http_status = 0; + gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + gpr.hr.ec = TALER_JSON_get_error_code (json); + gpr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + gpr.hr.ec = TALER_JSON_get_error_code (json); + gpr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) gpr.hr.ec); break; } pgh->cb (pgh->cb_cls, - &hr, - 0, - NULL); + &gpr); TALER_MERCHANT_products_get_cancel (pgh); } diff --git a/src/lib/merchant_api_get_reserve.c b/src/lib/merchant_api_get_reserve.c deleted file mode 100644 index 6c970db8..00000000 --- a/src/lib/merchant_api_get_reserve.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_reserve.c - * @brief Implementation of the GET /reserve request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * @brief A Handle for tracking wire reserve. - */ -struct TALER_MERCHANT_ReserveGetHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ReserveGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP GET /reserve request. - * - * @param cls the `struct TALER_MERCHANT_ReserveGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_reserve_get_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ReserveGetHandle *rgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - bool active; - - rgh->job = NULL; - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - struct TALER_MERCHANT_ReserveSummary rs; - const json_t *tips; - const char *exchange_url = NULL; - const char *payto_uri = NULL; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("creation_time", - &rs.creation_time), - GNUNET_JSON_spec_timestamp ("expiration_time", - &rs.expiration_time), - GNUNET_JSON_spec_bool ("active", - &active), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("exchange_url", - &exchange_url), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("payto_uri", - &payto_uri), - NULL), - TALER_JSON_spec_amount_any ("merchant_initial_amount", - &rs.merchant_initial_amount), - TALER_JSON_spec_amount_any ("exchange_initial_amount", - &rs.exchange_initial_amount), - TALER_JSON_spec_amount_any ("pickup_amount", - &rs.pickup_amount), - TALER_JSON_spec_amount_any ("committed_amount", - &rs.committed_amount), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - - tips = json_object_get (json, - "tips"); - if ((NULL == tips) || - json_is_null (tips)) - { - rgh->cb (rgh->cb_cls, - &hr, - &rs, - false, - NULL, - NULL, - 0, - NULL); - TALER_MERCHANT_reserve_get_cancel (rgh); - return; - } - if (! json_is_array (tips)) - { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - { - size_t tds_length; - json_t *tip; - struct TALER_MERCHANT_TipDetails *tds; - unsigned int i; - bool ok; - - tds_length = json_array_size (tips); - tds = GNUNET_new_array (tds_length, - struct TALER_MERCHANT_TipDetails); - ok = true; - json_array_foreach (tips, i, tip) { - struct TALER_MERCHANT_TipDetails *td = &tds[i]; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ("tip_id", - &td->tip_id), - TALER_JSON_spec_amount_any ("total_amount", - &td->amount), - GNUNET_JSON_spec_string ("reason", - &td->reason), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (tip, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - ok = false; - break; - } - } - - if (! ok) - { - GNUNET_break_op (0); - GNUNET_free (tds); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - rgh->cb (rgh->cb_cls, - &hr, - &rs, - active, - exchange_url, - payto_uri, - tds_length, - tds); - GNUNET_free (tds); - TALER_MERCHANT_reserve_get_cancel (rgh); - return; - } - } - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - response_code = 0; - break; - } - rgh->cb (rgh->cb_cls, - &hr, - NULL, - false, - NULL, - NULL, - 0, - NULL); - TALER_MERCHANT_reserve_get_cancel (rgh); -} - - -struct TALER_MERCHANT_ReserveGetHandle * -TALER_MERCHANT_reserve_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_ReservePublicKeyP *reserve_pub, - bool fetch_tips, - TALER_MERCHANT_ReserveGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ReserveGetHandle *rgh; - CURL *eh; - - rgh = GNUNET_new (struct TALER_MERCHANT_ReserveGetHandle); - rgh->ctx = ctx; - rgh->cb = cb; - rgh->cb_cls = cb_cls; - { - char res_str[sizeof (*reserve_pub) * 2]; - char arg_str[sizeof (res_str) + 32]; - char *end; - - end = GNUNET_STRINGS_data_to_string (reserve_pub, - sizeof (*reserve_pub), - res_str, - sizeof (res_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "private/reserves/%s", - res_str); - rgh->url = TALER_url_join (backend_url, - arg_str, - "tips", - fetch_tips ? "yes" : "no", - NULL); - } - if (NULL == rgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (rgh); - return NULL; - } - eh = TALER_MERCHANT_curl_easy_get_ (rgh->url); - rgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_reserve_get_finished, - rgh); - return rgh; -} - - -void -TALER_MERCHANT_reserve_get_cancel ( - struct TALER_MERCHANT_ReserveGetHandle *rgh) -{ - if (NULL != rgh->job) - { - GNUNET_CURL_job_cancel (rgh->job); - rgh->job = NULL; - } - GNUNET_free (rgh->url); - GNUNET_free (rgh); -} - - -/* end of merchant_api_get_reserve.c */ diff --git a/src/lib/merchant_api_get_reserves.c b/src/lib/merchant_api_get_reserves.c deleted file mode 100644 index 9b3b45e9..00000000 --- a/src/lib/merchant_api_get_reserves.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2017, 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_reserves.c - * @brief Implementation of the GET /reserves request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * @brief A Handle for tracking wire reserves. - */ -struct TALER_MERCHANT_ReservesGetHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ReservesGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP GET /reserves request. - * - * @param cls the `struct TALER_MERCHANT_ReservesGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_reserves_get_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ReservesGetHandle *rgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - rgh->job = NULL; - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - json_t *reserves; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("reserves", - &reserves), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - else - { - size_t rds_length; - struct TALER_MERCHANT_ReserveSummary *rds; - json_t *reserve; - unsigned int i; - bool ok; - - if (! json_is_array (reserves)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - rds_length = json_array_size (reserves); - rds = GNUNET_new_array (rds_length, - struct TALER_MERCHANT_ReserveSummary); - ok = true; - json_array_foreach (reserves, i, reserve) { - struct TALER_MERCHANT_ReserveSummary *rd = &rds[i]; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ("reserve_pub", - &rd->reserve_pub), - GNUNET_JSON_spec_timestamp ("creation_time", - &rd->creation_time), - GNUNET_JSON_spec_timestamp ("expiration_time", - &rd->expiration_time), - TALER_JSON_spec_amount_any ("merchant_initial_amount", - &rd->merchant_initial_amount), - TALER_JSON_spec_amount_any ("exchange_initial_amount", - &rd->exchange_initial_amount), - TALER_JSON_spec_amount_any ("pickup_amount", - &rd->pickup_amount), - TALER_JSON_spec_amount_any ("committed_amount", - &rd->committed_amount), - GNUNET_JSON_spec_bool ("active", - &rd->active), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (reserve, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - ok = false; - break; - } - } - - if (! ok) - { - GNUNET_break_op (0); - GNUNET_free (rds); - GNUNET_JSON_parse_free (spec); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - rgh->cb (rgh->cb_cls, - &hr, - rds_length, - rds); - GNUNET_free (rds); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_reserves_get_cancel (rgh); - return; - } - } - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - response_code = 0; - break; - } - rgh->cb (rgh->cb_cls, - &hr, - 0, - NULL); - TALER_MERCHANT_reserves_get_cancel (rgh); -} - - -struct TALER_MERCHANT_ReservesGetHandle * -TALER_MERCHANT_reserves_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - struct GNUNET_TIME_Timestamp after, - enum TALER_EXCHANGE_YesNoAll active, - enum TALER_EXCHANGE_YesNoAll failures, - TALER_MERCHANT_ReservesGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ReservesGetHandle *rgh; - CURL *eh; - const char *active_s = NULL; - const char *failures_s = NULL; - char *after_s; - - rgh = GNUNET_new (struct TALER_MERCHANT_ReservesGetHandle); - rgh->ctx = ctx; - rgh->cb = cb; - rgh->cb_cls = cb_cls; - active_s = TALER_yna_to_string (active); - failures_s = TALER_yna_to_string (failures); - after_s = GNUNET_strdup (GNUNET_TIME_timestamp2s (after)); - rgh->url = TALER_url_join (backend_url, - "private/reserves", - "active", - active_s, - "failures", - failures_s, - "after", - GNUNET_TIME_absolute_is_zero (after.abs_time) - ? NULL - : after_s, - NULL); - GNUNET_free (after_s); - if (NULL == rgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (rgh); - return NULL; - } - eh = TALER_MERCHANT_curl_easy_get_ (rgh->url); - rgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_reserves_get_finished, - rgh); - return rgh; -} - - -void -TALER_MERCHANT_reserves_get_cancel ( - struct TALER_MERCHANT_ReservesGetHandle *rgh) -{ - if (NULL != rgh->job) - { - GNUNET_CURL_job_cancel (rgh->job); - rgh->job = NULL; - } - GNUNET_free (rgh->url); - GNUNET_free (rgh); -} - - -/* end of merchant_api_get_reserves.c */ diff --git a/src/lib/merchant_api_get_template.c b/src/lib/merchant_api_get_template.c index 920333b6..9bbcc93a 100644 --- a/src/lib/merchant_api_get_template.c +++ b/src/lib/merchant_api_get_template.c @@ -79,9 +79,9 @@ handle_get_template_finished (void *cls, { struct TALER_MERCHANT_TemplateGetHandle *tgh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_TemplateGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; tgh->job = NULL; @@ -92,67 +92,55 @@ handle_get_template_finished (void *cls, { case MHD_HTTP_OK: { - const char *template_description; - const char *image; - json_t *template_contract; - bool rst_ok = true; + const json_t *contract; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("template_description", - &template_description), + &tgr.details.ok.template_description), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("image", - &image), + GNUNET_JSON_spec_string ("otp_id", + &tgr.details.ok.otp_id), NULL), - GNUNET_JSON_spec_json ("template_contract", - &template_contract), + GNUNET_JSON_spec_object_const ("template_contract", + &contract), GNUNET_JSON_spec_end () }; - - if ( (rst_ok) && - (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) ) + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) { + tgr.details.ok.template_contract = contract; tgh->cb (tgh->cb_cls, - &hr, - template_description, - image, - template_contract); - GNUNET_JSON_parse_free (spec); + &tgr); TALER_MERCHANT_template_get_cancel (tgh); return; } - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_JSON_parse_free (spec); + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) tgr.hr.ec); break; } tgh->cb (tgh->cb_cls, - &hr, - NULL, - NULL, - NULL); + &tgr); TALER_MERCHANT_template_get_cancel (tgh); } diff --git a/src/lib/merchant_api_get_templates.c b/src/lib/merchant_api_get_templates.c index 030d80cb..f1f973b5 100644 --- a/src/lib/merchant_api_get_templates.c +++ b/src/lib/merchant_api_get_templates.c @@ -32,6 +32,12 @@ /** + * Maximum number of templates we return. + */ +#define MAX_TEMPLATES 1024 + + +/** * Handle for a GET /templates operation. */ struct TALER_MERCHANT_TemplatesGetHandle @@ -68,53 +74,52 @@ struct TALER_MERCHANT_TemplatesGetHandle * Parse template information from @a ia. * * @param ia JSON array (or NULL!) with template data + * @param[in] tgr partially filled response * @param tgh operation handle * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue parse_templates (const json_t *ia, + struct TALER_MERCHANT_TemplatesGetResponse *tgr, struct TALER_MERCHANT_TemplatesGetHandle *tgh) { - unsigned int ies_len = json_array_size (ia); - struct TALER_MERCHANT_TemplateEntry ies[ies_len]; - size_t index; - json_t *value; - int ret; - - ret = GNUNET_OK; - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_TemplateEntry *ie = &ies[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("template_id", - &ie->template_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; + unsigned int tmpl_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) tmpl_len) || + (tmpl_len > MAX_TEMPLATES) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; } - if (GNUNET_OK == ret) { - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK - }; + struct TALER_MERCHANT_TemplateEntry tmpl[GNUNET_NZL (tmpl_len)]; + size_t index; + json_t *value; + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_TemplateEntry *ie = &tmpl[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("template_id", + &ie->template_id), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.templates_length = tmpl_len; + tgr->details.ok.templates = tmpl; tgh->cb (tgh->cb_cls, - &hr, - ies_len, - ies); + tgr); tgh->cb = NULL; /* just to be sure */ } - return ret; + return GNUNET_OK; } @@ -133,9 +138,9 @@ handle_get_templates_finished (void *cls, { struct TALER_MERCHANT_TemplatesGetHandle *tgh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_TemplatesGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; tgh->job = NULL; @@ -146,10 +151,10 @@ handle_get_templates_finished (void *cls, { case MHD_HTTP_OK: { - json_t *templates; + const json_t *templates; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("templates", - &templates), + GNUNET_JSON_spec_array_const ("templates", + &templates), GNUNET_JSON_spec_end () }; @@ -158,48 +163,39 @@ handle_get_templates_finished (void *cls, spec, NULL, NULL)) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - else + if (GNUNET_OK == + parse_templates (templates, + &tgr, + tgh)) { - if ( (! json_is_array (templates)) || - (GNUNET_OK == - parse_templates (templates, - tgh)) ) - { - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_templates_get_cancel (tgh); - return; - } - else - { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } + TALER_MERCHANT_templates_get_cancel (tgh); + return; } - GNUNET_JSON_parse_free (spec); + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) tgr.hr.ec); break; } tgh->cb (tgh->cb_cls, - &hr, - 0, - NULL); + &tgr); TALER_MERCHANT_templates_get_cancel (tgh); } diff --git a/src/lib/merchant_api_get_tips.c b/src/lib/merchant_api_get_tips.c deleted file mode 100644 index 7af6936b..00000000 --- a/src/lib/merchant_api_get_tips.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_tips.c - * @brief Implementation of the GET /private/tips request of the merchant's HTTP API - * @author Jonathan Buchanan - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /private/tips operation. - */ -struct TALER_MERCHANT_TipsGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipsGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse tip information from @a ia. - * - * @param ia JSON array (or NULL!) tip order data - * @param tgh operation handle - * @return #GNUNET_OK on success - */ -static int -parse_tips (const json_t *ia, - struct TALER_MERCHANT_TipsGetHandle *tgh) -{ - unsigned int tes_len = json_array_size (ia); - struct TALER_MERCHANT_TipEntry tes[tes_len]; - size_t index; - json_t *value; - int ret; - - ret = GNUNET_OK; - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_TipEntry *ie = &tes[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint64 ("row_id", - &ie->row_id), - GNUNET_JSON_spec_fixed_auto ("tip_id", - &ie->tip_id), - TALER_JSON_spec_amount_any ("tip_amount", - &ie->tip_amount), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; - } - if (GNUNET_OK == ret) - { - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK - }; - - tgh->cb (tgh->cb_cls, - &hr, - tes_len, - tes); - tgh->cb = NULL; /* just to be sure */ - } - return ret; -} - - -/** - * Function called when we're done processing the - * HTTP GET /private/tips request. - * - * @param cls the `struct TALER_MERCHANT_TipsGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_tips_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipsGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /private/tips response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - json_t *tips; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("tips", - &tips), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } - else - { - if ( (! json_is_array (tips)) || - (GNUNET_OK == - parse_tips (tips, - tgh)) ) - { - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_tips_get_cancel (tgh); - return; - } - else - { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } - } - GNUNET_JSON_parse_free (spec); - break; - } - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &hr, - 0, - NULL); - TALER_MERCHANT_tips_get_cancel (tgh); -} - - -struct TALER_MERCHANT_TipsGetHandle * -TALER_MERCHANT_tips_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_TipsGetCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_tips_get2 (ctx, - backend_url, - TALER_EXCHANGE_YNA_NO, - -20, - UINT64_MAX, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_TipsGetHandle * -TALER_MERCHANT_tips_get2 (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - enum TALER_EXCHANGE_YesNoAll expired, - int64_t limit, - uint64_t offset, - TALER_MERCHANT_TipsGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TipsGetHandle *tgh; - CURL *eh; - - GNUNET_assert (NULL != backend_url); - if (0 == limit) - { - GNUNET_break (0); - return NULL; - } - tgh = GNUNET_new (struct TALER_MERCHANT_TipsGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - - /* build tgh->url with the various optional arguments */ - { - char cbuf[30]; - char lbuf[30]; - bool have_offset; - - GNUNET_snprintf (lbuf, - sizeof (lbuf), - "%lld", - (long long) limit); - - if (limit > 0) - have_offset = (0 != offset); - else - have_offset = (UINT64_MAX != offset); - GNUNET_snprintf (cbuf, - sizeof (cbuf), - "%llu", - (unsigned long long) offset); - tgh->url = TALER_url_join (backend_url, - "private/tips", - "expired", - (TALER_EXCHANGE_YNA_ALL != expired) - ? TALER_yna_to_string (expired) - : NULL, - "offset", (have_offset) ? cbuf : NULL, - "limit", (-20 != limit) ? lbuf : NULL, - NULL); - } - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_tips_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_tips_get_cancel ( - struct TALER_MERCHANT_TipsGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_get_tokenfamily.c b/src/lib/merchant_api_get_tokenfamily.c new file mode 100644 index 00000000..d7e6b06e --- /dev/null +++ b/src/lib/merchant_api_get_tokenfamily.c @@ -0,0 +1,210 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_tokenfamily.c + * @brief Implementation of the GET /tokenfamily/$ID request of the merchant's HTTP API + * @author Christian Blättler + */ +#include "platform.h" +#include <curl/curl.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /tokenfamilies/$SLUG operation. + */ +struct TALER_MERCHANT_TokenFamilyGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TokenFamilyGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /tokenfamilies/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TokenFamilyGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_token_family_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TokenFamilyGetHandle *handle = cls; + const json_t *json = response; + struct TALER_MERCHANT_TokenFamilyGetResponse res = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + handle->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /tokenfamilies/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + // Parse token family response + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("slug", + &res.details.ok.slug), + GNUNET_JSON_spec_string ("name", + &res.details.ok.name), + GNUNET_JSON_spec_string ("description", + &res.details.ok.description), + GNUNET_JSON_spec_object_const ("description_i18n", + &res.details.ok.description_i18n), + GNUNET_JSON_spec_timestamp ("valid_after", + &res.details.ok.valid_after), + GNUNET_JSON_spec_timestamp ("valid_before", + &res.details.ok.valid_before), + GNUNET_JSON_spec_relative_time ("duation", + &res.details.ok.duration), + GNUNET_JSON_spec_string ("kind", + &res.details.ok.kind), + GNUNET_JSON_spec_uint64 ("issued", + &res.details.ok.issued), + GNUNET_JSON_spec_uint64 ("redeemed", + &res.details.ok.redeemed), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + handle->cb (handle->cb_cls, + &res); + GNUNET_JSON_parse_free (spec); + TALER_MERCHANT_token_family_get_cancel (handle); + return; + } + res.hr.http_status = 0; + res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) res.hr.ec); + break; + } +} + +struct TALER_MERCHANT_TokenFamilyGetHandle * +TALER_MERCHANT_token_family_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *token_family_slug, + TALER_MERCHANT_TokenFamilyGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TokenFamilyGetHandle *handle; + CURL *eh; + + handle = GNUNET_new (struct TALER_MERCHANT_TokenFamilyGetHandle); + handle->ctx = ctx; + handle->cb = cb; + handle->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/tokenfamilies/%s", + token_family_slug); + handle->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == handle->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (handle); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + handle->url); + eh = TALER_MERCHANT_curl_easy_get_ (handle->url); + handle->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_token_family_finished, + handle); + return handle; +} + +void +TALER_MERCHANT_token_family_get_cancel ( + struct TALER_MERCHANT_TokenFamilyGetHandle *handle) +{ + if (NULL != handle->job) + GNUNET_CURL_job_cancel (handle->job); + GNUNET_free (handle->url); + GNUNET_free (handle); +}
\ No newline at end of file diff --git a/src/lib/merchant_api_get_transfers.c b/src/lib/merchant_api_get_transfers.c index f2f1186d..6116a54f 100644 --- a/src/lib/merchant_api_get_transfers.c +++ b/src/lib/merchant_api_get_transfers.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2017, 2020 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -81,23 +81,23 @@ handle_transfers_get_finished (void *cls, { struct TALER_MERCHANT_GetTransfersHandle *gth = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_GetTransfersResponse gtr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; gth->job = NULL; switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: { - json_t *transfers; + const json_t *transfers; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("transfers", - &transfers), + GNUNET_JSON_spec_array_const ("transfers", + &transfers), GNUNET_JSON_spec_end () }; @@ -107,26 +107,18 @@ handle_transfers_get_finished (void *cls, NULL, NULL)) { GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + gtr.hr.http_status = 0; + gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } - else + { size_t tds_length; struct TALER_MERCHANT_TransferData *tds; json_t *transfer; - unsigned int i; + size_t i; bool ok; - if (! json_is_array (transfers)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } tds_length = json_array_size (transfers); tds = GNUNET_new_array (tds_length, struct TALER_MERCHANT_TransferData); @@ -138,9 +130,9 @@ handle_transfers_get_finished (void *cls, &td->credit_amount), GNUNET_JSON_spec_fixed_auto ("wtid", &td->wtid), - GNUNET_JSON_spec_string ("payto_uri", - &td->payto_uri), - GNUNET_JSON_spec_string ("exchange_url", + TALER_JSON_spec_payto_uri ("payto_uri", + &td->payto_uri), + TALER_JSON_spec_web_url ("exchange_url", &td->exchange_url), GNUNET_JSON_spec_uint64 ("transfer_serial_id", &td->credit_serial), @@ -174,55 +166,51 @@ handle_transfers_get_finished (void *cls, { GNUNET_break_op (0); GNUNET_free (tds); - GNUNET_JSON_parse_free (spec); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + gtr.hr.http_status = 0; + gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } + gtr.details.ok.transfers = tds; + gtr.details.ok.transfers_length = tds_length; gth->cb (gth->cb_cls, - &hr, - tds_length, - tds); + >r); GNUNET_free (tds); - GNUNET_JSON_parse_free (spec); TALER_MERCHANT_transfers_get_cancel (gth); return; } } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + gtr.hr.ec = TALER_JSON_get_error_code (json); + gtr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + gtr.hr.ec = TALER_JSON_get_error_code (json); + gtr.hr.hint = TALER_JSON_get_error_hint (json); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + gtr.hr.ec = TALER_JSON_get_error_code (json); + gtr.hr.hint = TALER_JSON_get_error_hint (json); break; default: /* unexpected response code */ GNUNET_break_op (0); TALER_MERCHANT_parse_error_details_ (json, response_code, - &hr); + >r.hr); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); - response_code = 0; + (int) gtr.hr.ec); + gtr.hr.http_status = 0; break; } gth->cb (gth->cb_cls, - &hr, - 0, - NULL); + >r); TALER_MERCHANT_transfers_get_cancel (gth); } diff --git a/src/lib/merchant_api_get_webhook.c b/src/lib/merchant_api_get_webhook.c index 0e9abc6f..551aa915 100644 --- a/src/lib/merchant_api_get_webhook.c +++ b/src/lib/merchant_api_get_webhook.c @@ -101,7 +101,7 @@ handle_get_webhook_finished (void *cls, struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("event_type", &event_type), - GNUNET_JSON_spec_string ("url", + TALER_JSON_spec_web_url ("url", &url), GNUNET_JSON_spec_string ("http_method", &http_method), diff --git a/src/lib/merchant_api_get_webhooks.c b/src/lib/merchant_api_get_webhooks.c index f5ba6f41..e702baac 100644 --- a/src/lib/merchant_api_get_webhooks.c +++ b/src/lib/merchant_api_get_webhooks.c @@ -32,6 +32,11 @@ /** + * Maximum number of webhooks we return. + */ +#define MAX_WEBHOOKS 1024 + +/** * Handle for a GET /webhooks operation. */ struct TALER_MERCHANT_WebhooksGetHandle @@ -68,53 +73,52 @@ struct TALER_MERCHANT_WebhooksGetHandle * Parse webhook information from @a ia. * * @param ia JSON array (or NULL!) with webhook data + * @param[in] wgr partially filled webhook response * @param wgh operation handle * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue parse_webhooks (const json_t *ia, + struct TALER_MERCHANT_WebhooksGetResponse *wgr, struct TALER_MERCHANT_WebhooksGetHandle *wgh) { - unsigned int ies_len = json_array_size (ia); - struct TALER_MERCHANT_WebhookEntry ies[ies_len]; - size_t index; - json_t *value; - int ret; - - ret = GNUNET_OK; - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_WebhookEntry *ie = &ies[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("webhook_id", - &ie->webhook_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; + unsigned int whook_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) whook_len) || + (whook_len > MAX_WEBHOOKS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; } - if (GNUNET_OK == ret) { - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK - }; + struct TALER_MERCHANT_WebhookEntry whook[GNUNET_NZL (whook_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_WebhookEntry *ie = &whook[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("webhook_id", + &ie->webhook_id), + GNUNET_JSON_spec_end () + }; + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + wgr->details.ok.webhooks_length = whook_len; + wgr->details.ok.webhooks = whook; wgh->cb (wgh->cb_cls, - &hr, - ies_len, - ies); + wgr); wgh->cb = NULL; /* just to be sure */ } - return ret; + return GNUNET_OK; } @@ -133,9 +137,9 @@ handle_get_webhooks_finished (void *cls, { struct TALER_MERCHANT_WebhooksGetHandle *wgh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_WebhooksGetResponse wgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; wgh->job = NULL; @@ -146,10 +150,10 @@ handle_get_webhooks_finished (void *cls, { case MHD_HTTP_OK: { - json_t *webhooks; + const json_t *webhooks; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("webhooks", - &webhooks), + GNUNET_JSON_spec_array_const ("webhooks", + &webhooks), GNUNET_JSON_spec_end () }; @@ -158,48 +162,39 @@ handle_get_webhooks_finished (void *cls, spec, NULL, NULL)) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + wgr.hr.http_status = 0; + wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - else + if (GNUNET_OK == + parse_webhooks (webhooks, + &wgr, + wgh)) { - if ( (! json_is_array (webhooks)) || - (GNUNET_OK == - parse_webhooks (webhooks, - wgh)) ) - { - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_webhooks_get_cancel (wgh); - return; - } - else - { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } + TALER_MERCHANT_webhooks_get_cancel (wgh); + return; } - GNUNET_JSON_parse_free (spec); + wgr.hr.http_status = 0; + wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + wgr.hr.ec = TALER_JSON_get_error_code (json); + wgr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + wgr.hr.ec = TALER_JSON_get_error_code (json); + wgr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) wgr.hr.ec); break; } wgh->cb (wgh->cb_cls, - &hr, - 0, - NULL); + &wgr); TALER_MERCHANT_webhooks_get_cancel (wgh); } diff --git a/src/lib/merchant_api_merchant_get_order.c b/src/lib/merchant_api_merchant_get_order.c index 0e6b53bf..3bd4003b 100644 --- a/src/lib/merchant_api_merchant_get_order.c +++ b/src/lib/merchant_api_merchant_get_order.c @@ -34,6 +34,17 @@ /** + * Maximum number of refund details we return. + */ +#define MAX_REFUND_DETAILS 1024 + +/** + * Maximum number of wire details we return. + */ +#define MAX_WIRE_DETAILS 1024 + + +/** * @brief A GET /private/orders/$ORDER handle */ struct TALER_MERCHANT_OrderMerchantGetHandle @@ -81,21 +92,21 @@ handle_unpaid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount_any ( "total_amount", - &osr->details.success.details.unpaid.contract_amount), + &osr->details.ok.details.unpaid.contract_amount), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ( "already_paid_order_id", - &osr->details.success.details.unpaid.already_paid_order_id), + &osr->details.ok.details.unpaid.already_paid_order_id), NULL), GNUNET_JSON_spec_string ( "taler_pay_uri", - &osr->details.success.details.unpaid.taler_pay_uri), + &osr->details.ok.details.unpaid.taler_pay_uri), GNUNET_JSON_spec_string ( "summary", - &osr->details.success.details.unpaid.summary), + &osr->details.ok.details.unpaid.summary), GNUNET_JSON_spec_timestamp ( "creation_time", - &osr->details.success.details.unpaid.creation_time), + &osr->details.ok.details.unpaid.creation_time), GNUNET_JSON_spec_end () }; @@ -111,7 +122,7 @@ handle_unpaid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, osr); return; } - osr->details.success.status = TALER_MERCHANT_OSC_UNPAID; + osr->details.ok.status = TALER_MERCHANT_OSC_UNPAID; omgh->cb (omgh->cb_cls, osr); } @@ -130,9 +141,9 @@ handle_claimed (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, struct TALER_MERCHANT_OrderStatusResponse *osr) { struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("contract_terms", - (json_t **) &osr->details.success.details.claimed. - contract_terms), + GNUNET_JSON_spec_object_const ( + "contract_terms", + &osr->details.ok.details.claimed.contract_terms), GNUNET_JSON_spec_end () }; @@ -148,10 +159,9 @@ handle_claimed (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, osr); return; } - osr->details.success.status = TALER_MERCHANT_OSC_CLAIMED; + osr->details.ok.status = TALER_MERCHANT_OSC_CLAIMED; omgh->cb (omgh->cb_cls, osr); - GNUNET_JSON_parse_free (spec); } @@ -167,35 +177,36 @@ static void handle_paid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, struct TALER_MERCHANT_OrderStatusResponse *osr) { - uint32_t ec32; uint32_t hc32; - json_t *wire_details; - json_t *wire_reports; - json_t *refund_details; + const json_t *wire_details; + const json_t *refund_details; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_bool ("refunded", - &osr->details.success.details.paid.refunded), + &osr->details.ok.details.paid.refunded), GNUNET_JSON_spec_bool ("refund_pending", - &osr->details.success.details.paid.refund_pending), + &osr->details.ok.details.paid.refund_pending), GNUNET_JSON_spec_bool ("wired", - &osr->details.success.details.paid.wired), + &osr->details.ok.details.paid.wired), TALER_JSON_spec_amount_any ("deposit_total", - &osr->details.success.details.paid.deposit_total), - GNUNET_JSON_spec_uint32 ("exchange_code", - &ec32), + &osr->details.ok.details.paid.deposit_total), + TALER_JSON_spec_ec ("exchange_code", + &osr->details.ok.details.paid.exchange_ec), GNUNET_JSON_spec_uint32 ("exchange_http_status", &hc32), TALER_JSON_spec_amount_any ("refund_amount", - &osr->details.success.details.paid.refund_amount), - GNUNET_JSON_spec_json ( + &osr->details.ok.details.paid.refund_amount), + GNUNET_JSON_spec_object_const ( "contract_terms", - (json_t **) &osr->details.success.details.paid.contract_terms), - GNUNET_JSON_spec_json ("wire_details", - &wire_details), - GNUNET_JSON_spec_json ("wire_reports", - &wire_reports), - GNUNET_JSON_spec_json ("refund_details", - &refund_details), + &osr->details.ok.details.paid.contract_terms), + GNUNET_JSON_spec_array_const ("wire_details", + &wire_details), + GNUNET_JSON_spec_array_const ("refund_details", + &refund_details), + /* Only available since **v14** */ + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_timestamp ("last_payment", + &osr->details.ok.details.paid.last_payment), + NULL), GNUNET_JSON_spec_end () }; @@ -211,143 +222,107 @@ handle_paid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, osr); return; } - if (! (json_is_array (wire_details) && - json_is_array (wire_reports) && - json_is_array (refund_details) && - json_is_object (osr->details.success.details.paid.contract_terms)) ) - { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - GNUNET_JSON_parse_free (spec); - return; - } - osr->details.success.status = TALER_MERCHANT_OSC_PAID; - osr->details.success.details.paid.exchange_ec = (enum TALER_ErrorCode) ec32; - osr->details.success.details.paid.exchange_hc = (unsigned int) hc32; + osr->details.ok.status = TALER_MERCHANT_OSC_PAID; + + osr->details.ok.details.paid.exchange_hc = (unsigned int) hc32; { - unsigned int wts_len = json_array_size (wire_details); - unsigned int wrs_len = json_array_size (wire_reports); - unsigned int ref_len = json_array_size (refund_details); - struct TALER_MERCHANT_WireTransfer wts[wts_len]; - struct TALER_MERCHANT_WireReport wrs[wrs_len]; - struct TALER_MERCHANT_RefundOrderDetail ref[ref_len]; - - for (unsigned int i = 0; i<wts_len; i++) + unsigned int wts_len = (unsigned int) json_array_size (wire_details); + unsigned int ref_len = (unsigned int) json_array_size (refund_details); + + if ( (json_array_size (wire_details) != (size_t) wts_len) || + (wts_len > MAX_WIRE_DETAILS) ) { - struct TALER_MERCHANT_WireTransfer *wt = &wts[i]; - const json_t *w = json_array_get (wire_details, - i); - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("exchange_url", - &wt->exchange_url), - GNUNET_JSON_spec_fixed_auto ("wtid", - &wt->wtid), - GNUNET_JSON_spec_timestamp ("execution_time", - &wt->execution_time), - TALER_JSON_spec_amount_any ("amount", - &wt->total_amount), - GNUNET_JSON_spec_bool ("confirmed", - &wt->confirmed), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (w, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - GNUNET_JSON_parse_free (spec); - return; - } + GNUNET_break (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; + omgh->cb (omgh->cb_cls, + osr); + return; + } + if ( (json_array_size (refund_details) != (size_t) ref_len) || + (ref_len > MAX_REFUND_DETAILS) ) + { + GNUNET_break (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; + omgh->cb (omgh->cb_cls, + osr); + return; } - - for (unsigned int i = 0; i<wrs_len; i++) { - struct TALER_MERCHANT_WireReport *wr = &wrs[i]; - const json_t *w = json_array_get (wire_reports, i); - uint32_t c32; - uint32_t eec32; - uint32_t ehs32; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_uint32 ("code", - &c32), - GNUNET_JSON_spec_string ("hint", - &wr->hint), - GNUNET_JSON_spec_uint32 ("exchange_code", - &eec32), - GNUNET_JSON_spec_uint32 ("exchange_http_status", - &ehs32), - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &wr->coin_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (w, - ispec, - NULL, NULL)) + struct TALER_MERCHANT_WireTransfer wts[GNUNET_NZL (wts_len)]; + struct TALER_MERCHANT_RefundOrderDetail ref[GNUNET_NZL (ref_len)]; + + for (unsigned int i = 0; i<wts_len; i++) { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - GNUNET_JSON_parse_free (spec); - return; + struct TALER_MERCHANT_WireTransfer *wt = &wts[i]; + const json_t *w = json_array_get (wire_details, + i); + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_web_url ("exchange_url", + &wt->exchange_url), + GNUNET_JSON_spec_fixed_auto ("wtid", + &wt->wtid), + GNUNET_JSON_spec_timestamp ("execution_time", + &wt->execution_time), + TALER_JSON_spec_amount_any ("amount", + &wt->total_amount), + GNUNET_JSON_spec_bool ("confirmed", + &wt->confirmed), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (w, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + osr); + return; + } } - wr->code = (enum TALER_ErrorCode) c32; - wr->hr.ec = (enum TALER_ErrorCode) eec32; - wr->hr.http_status = (unsigned int) ehs32; - } - for (unsigned int i = 0; i<ref_len; i++) - { - struct TALER_MERCHANT_RefundOrderDetail *ro = &ref[i]; - const json_t *w = json_array_get (refund_details, - i); - struct GNUNET_JSON_Specification ispec[] = { - TALER_JSON_spec_amount_any ("amount", - &ro->refund_amount), - GNUNET_JSON_spec_string ("reason", - &ro->reason), - GNUNET_JSON_spec_timestamp ("timestamp", - &ro->refund_time), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (w, - ispec, - NULL, NULL)) + for (unsigned int i = 0; i<ref_len; i++) { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - GNUNET_JSON_parse_free (spec); - return; + struct TALER_MERCHANT_RefundOrderDetail *ro = &ref[i]; + const json_t *w = json_array_get (refund_details, + i); + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_amount_any ("amount", + &ro->refund_amount), + GNUNET_JSON_spec_string ("reason", + &ro->reason), + GNUNET_JSON_spec_timestamp ("timestamp", + &ro->refund_time), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (w, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + osr); + return; + } } - } - osr->details.success.details.paid.wts = wts; - osr->details.success.details.paid.wts_len = wts_len; - osr->details.success.details.paid.wrs = wrs; - osr->details.success.details.paid.wrs_len = wrs_len; - osr->details.success.details.paid.refunds = ref; - osr->details.success.details.paid.refunds_len = ref_len; - omgh->cb (omgh->cb_cls, - osr); + osr->details.ok.details.paid.wts = wts; + osr->details.ok.details.paid.wts_len = wts_len; + osr->details.ok.details.paid.refunds = ref; + osr->details.ok.details.paid.refunds_len = ref_len; + omgh->cb (omgh->cb_cls, + osr); + } } - GNUNET_JSON_parse_free (spec); } @@ -464,27 +439,20 @@ handle_merchant_order_get_finished (void *cls, struct TALER_MERCHANT_OrderMerchantGetHandle * -TALER_MERCHANT_merchant_order_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *order_id, - const char *session_id, - bool transfer, - struct GNUNET_TIME_Relative timeout, - TALER_MERCHANT_OrderMerchantGetCallback cb, - void *cb_cls) +TALER_MERCHANT_merchant_order_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *order_id, + const char *session_id, + struct GNUNET_TIME_Relative timeout, + TALER_MERCHANT_OrderMerchantGetCallback cb, + void *cb_cls) { struct TALER_MERCHANT_OrderMerchantGetHandle *omgh; - unsigned long long tms; - long tlong; - - tms = (unsigned long long) (timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); - /* set curl timeout to *our* long poll timeout plus one minute - (for network latency and processing delays) */ - tlong = (long) (GNUNET_TIME_relative_add (timeout, - GNUNET_TIME_UNIT_MINUTES). - rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); + unsigned int tms; + + tms = (unsigned int) (timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); omgh = GNUNET_new (struct TALER_MERCHANT_OrderMerchantGetHandle); omgh->ctx = ctx; omgh->cb = cb; @@ -495,7 +463,7 @@ TALER_MERCHANT_merchant_order_get (struct GNUNET_CURL_Context *ctx, GNUNET_snprintf (timeout_ms, sizeof (timeout_ms), - "%llu", + "%u", tms); GNUNET_asprintf (&path, "private/orders/%s", @@ -503,7 +471,6 @@ TALER_MERCHANT_merchant_order_get (struct GNUNET_CURL_Context *ctx, omgh->url = TALER_url_join (backend_url, path, "session_id", session_id, - "transfer", transfer ? "YES" : "NO", "timeout_ms", (0 != tms) ? timeout_ms : NULL, NULL); GNUNET_free (path); @@ -527,16 +494,12 @@ TALER_MERCHANT_merchant_order_get (struct GNUNET_CURL_Context *ctx, GNUNET_free (omgh); return NULL; } - if (CURLE_OK != - curl_easy_setopt (eh, - CURLOPT_TIMEOUT_MS, - tlong)) + if (0 != tms) { - GNUNET_break (0); - curl_easy_cleanup (eh); - GNUNET_free (omgh->url); - GNUNET_free (omgh); - return NULL; + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 100L))); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, diff --git a/src/lib/merchant_api_merchant_get_tip.c b/src/lib/merchant_api_merchant_get_tip.c deleted file mode 100644 index 7466fccc..00000000 --- a/src/lib/merchant_api_merchant_get_tip.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_merchant_get_tip.c - * @brief Implementation of the GET /private/tips/$TIP_ID request of the merchant's HTTP API - * @author Jonathan Buchanan - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -struct TALER_MERCHANT_TipMerchantGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipMerchantGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -static enum GNUNET_GenericReturnValue -parse_pickups (const json_t *pa, - struct TALER_MERCHANT_TipStatusResponse *tsr, - struct TALER_MERCHANT_TipMerchantGetHandle *tgh) -{ - unsigned int pa_len = json_array_size (pa); - struct TALER_MERCHANT_PickupDetail pickups[pa_len]; - size_t index; - json_t *value; - - json_array_foreach (pa, index, value) - { - struct TALER_MERCHANT_PickupDetail *pickup = &pickups[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("pickup_id", - &pickup->pickup_id), - GNUNET_JSON_spec_uint64 ("num_planchets", - &pickup->num_planchets), - TALER_JSON_spec_amount_any ("requested_amount", - &pickup->requested_amount), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - tsr->details.success.pickups_length = pa_len; - tsr->details.success.pickups = pickups; - tgh->cb (tgh->cb_cls, - tsr); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * GET /private/tips/$TIP_ID request. - * - * @param cls the `struct TALER_MERCHANT_TipMerchantGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_merchant_tip_get_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipMerchantGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_TipStatusResponse tsr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /private/tips/$TIP_ID response with status code %u\n", - (unsigned int) response_code); - tgh->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ("total_authorized", - &tsr.details.success.total_authorized), - TALER_JSON_spec_amount_any ("total_picked_up", - &tsr.details.success.total_picked_up), - GNUNET_JSON_spec_string ("reason", - &tsr.details.success.reason), - GNUNET_JSON_spec_timestamp ("expiration", - &tsr.details.success.expiration), - GNUNET_JSON_spec_fixed_auto ("reserve_pub", - &tsr.details.success.reserve_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tsr.hr.http_status = 0; - tsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } - else - { - json_t *pickups = json_object_get (json, - "pickups"); - if (! json_is_array (pickups)) - { - tgh->cb (tgh->cb_cls, - &tsr); - TALER_MERCHANT_merchant_tip_get_cancel (tgh); - return; - } - if (GNUNET_OK == - parse_pickups (pickups, - &tsr, - tgh)) - { - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_merchant_tip_get_cancel (tgh); - return; - } - tsr.hr.http_status = 0; - tsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } - GNUNET_JSON_parse_free (spec); - break; - } - case MHD_HTTP_UNAUTHORIZED: - tsr.hr.ec = TALER_JSON_get_error_code (json); - tsr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* legal, can happen if instance or tip reserve is unknown */ - tsr.hr.ec = TALER_JSON_get_error_code (json); - tsr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - tsr.hr.ec = TALER_JSON_get_error_code (json); - tsr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &tsr.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tsr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tsr); - TALER_MERCHANT_merchant_tip_get_cancel (tgh); -} - - -struct TALER_MERCHANT_TipMerchantGetHandle * -TALER_MERCHANT_merchant_tip_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_TipIdentifierP *tip_id, - const struct TALER_Amount *min_pick_up, - struct GNUNET_TIME_Relative lp_timeout, - bool pickups, - TALER_MERCHANT_TipMerchantGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TipMerchantGetHandle *tgh; - CURL *eh; - - GNUNET_assert (NULL != backend_url); - tgh = GNUNET_new (struct TALER_MERCHANT_TipMerchantGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - - { - char res_str[sizeof (*tip_id) * 2]; - char arg_str[sizeof (res_str) + 48]; - char timeout_str[32]; - char *end; - - end = GNUNET_STRINGS_data_to_string (tip_id, - sizeof (*tip_id), - res_str, - sizeof (res_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "private/tips/%s", - res_str); - GNUNET_snprintf (timeout_str, - sizeof (timeout_str), - "%llu", - ((unsigned long long) - lp_timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us)); - tgh->url = TALER_url_join (backend_url, - arg_str, - "pickups", - pickups - ? "yes" - : NULL, - "min_amount", - min_pick_up - ? TALER_amount2s (min_pick_up) - : NULL, - "timeout_ms", - GNUNET_TIME_relative_is_zero (lp_timeout) - ? NULL - : timeout_str, - NULL); - } - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_merchant_tip_get_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_merchant_tip_get_cancel ( - struct TALER_MERCHANT_TipMerchantGetHandle *tgh) -{ - if (NULL != tgh->job) - { - GNUNET_CURL_job_cancel (tgh->job); - tgh->job = NULL; - } - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} - - -/* end of merchant_api_merchant_get_tip.c */ diff --git a/src/lib/merchant_api_patch_account.c b/src/lib/merchant_api_patch_account.c new file mode 100644 index 00000000..ce0e74d4 --- /dev/null +++ b/src/lib/merchant_api_patch_account.c @@ -0,0 +1,254 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch_account.c + * @brief Implementation of the PATCH /accounts/$ID request + * of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountPatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountPatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /accounts/$ID request. + * + * @param cls the `struct TALER_MERCHANT_AccountPatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountPatchHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /accounts/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_account_patch_cancel (tph); +} + + +struct TALER_MERCHANT_AccountPatchHandle * +TALER_MERCHANT_account_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MerchantWireHashP *h_wire, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + TALER_MERCHANT_AccountPatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountPatchHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("credit_facade_url", + credit_facade_url)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("credit_facade_credentials", + (json_t *) credit_facade_credentials))); + tph = GNUNET_new (struct TALER_MERCHANT_AccountPatchHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + { + char w_str[sizeof (*h_wire) * 2]; + char *path; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_wire, + sizeof (*h_wire), + w_str, + sizeof (w_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "private/accounts/%s", + w_str); + tph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_patch_account_finished, + tph); + } + return tph; +} + + +void +TALER_MERCHANT_account_patch_cancel ( + struct TALER_MERCHANT_AccountPatchHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_patch_account.c */ diff --git a/src/lib/merchant_api_patch_instance.c b/src/lib/merchant_api_patch_instance.c index 0613a852..420cd549 100644 --- a/src/lib/merchant_api_patch_instance.c +++ b/src/lib/merchant_api_patch_instance.c @@ -31,6 +31,7 @@ #include "merchant_api_curl_defaults.h" #include "merchant_api_common.h" #include <taler/taler_json_lib.h> +#include <taler/taler_kyclogic_lib.h> #include <taler/taler_curl_lib.h> @@ -157,55 +158,37 @@ TALER_MERCHANT_instance_patch ( struct GNUNET_CURL_Context *ctx, const char *backend_url, const char *instance_id, - unsigned int accounts_length, - const char *payto_uris[], const char *name, + enum TALER_KYCLOGIC_KycUserType ut, const json_t *address, const json_t *jurisdiction, - const struct TALER_Amount *default_max_wire_fee, - uint32_t default_wire_fee_amortization, - const struct TALER_Amount *default_max_deposit_fee, + bool use_stefan, struct GNUNET_TIME_Relative default_wire_transfer_delay, struct GNUNET_TIME_Relative default_pay_delay, TALER_MERCHANT_InstancePatchCallback cb, void *cb_cls) { struct TALER_MERCHANT_InstancePatchHandle *iph; - json_t *jpayto_uris; json_t *req_obj; + const char *uts; - jpayto_uris = json_array (); - if (NULL == jpayto_uris) + uts = TALER_KYCLOGIC_kyc_user_type2s (ut); + if (NULL == uts) { GNUNET_break (0); return NULL; } - for (unsigned int i = 0; i<accounts_length; i++) - { - if (0 != - json_array_append_new (jpayto_uris, - json_string (payto_uris[i]))) - { - GNUNET_break (0); - json_decref (jpayto_uris); - return NULL; - } - } req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("payto_uris", - jpayto_uris), GNUNET_JSON_pack_string ("name", name), + GNUNET_JSON_pack_string ("user_type", + uts), GNUNET_JSON_pack_object_incref ("address", (json_t *) address), GNUNET_JSON_pack_object_incref ("jurisdiction", (json_t *) jurisdiction), - TALER_JSON_pack_amount ("default_max_wire_fee", - default_max_wire_fee), - GNUNET_JSON_pack_uint64 ("default_wire_fee_amortization", - default_wire_fee_amortization), - TALER_JSON_pack_amount ("default_max_deposit_fee", - default_max_deposit_fee), + GNUNET_JSON_pack_bool ("use_stefan", + use_stefan), GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay", default_wire_transfer_delay), GNUNET_JSON_pack_time_rel ("default_pay_delay", diff --git a/src/lib/merchant_api_patch_order_forget.c b/src/lib/merchant_api_patch_order_forget.c index 572f00e6..118655db 100644 --- a/src/lib/merchant_api_patch_order_forget.c +++ b/src/lib/merchant_api_patch_order_forget.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020, 2021 Taler Systems SA + Copyright (C) 2020, 2021, 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -150,13 +150,14 @@ handle_forget_finished (void *cls, struct TALER_MERCHANT_OrderForgetHandle * -TALER_MERCHANT_order_forget (struct GNUNET_CURL_Context *ctx, - const char *merchant_url, - const char *order_id, - unsigned int fields_length, - const char *fields[], - TALER_MERCHANT_ForgetCallback cb, - void *cb_cls) +TALER_MERCHANT_order_forget ( + struct GNUNET_CURL_Context *ctx, + const char *merchant_url, + const char *order_id, + unsigned int fields_length, + const char *fields[static fields_length], + TALER_MERCHANT_ForgetCallback cb, + void *cb_cls) { struct TALER_MERCHANT_OrderForgetHandle *ofh; json_t *req_fields; @@ -236,8 +237,8 @@ TALER_MERCHANT_order_forget (struct GNUNET_CURL_Context *ctx, void -TALER_MERCHANT_order_forget_cancel (struct - TALER_MERCHANT_OrderForgetHandle *ofh) +TALER_MERCHANT_order_forget_cancel ( + struct TALER_MERCHANT_OrderForgetHandle *ofh) { if (NULL != ofh->job) { diff --git a/src/lib/merchant_api_patch_otp_device.c b/src/lib/merchant_api_patch_otp_device.c new file mode 100644 index 00000000..322efe7b --- /dev/null +++ b/src/lib/merchant_api_patch_otp_device.c @@ -0,0 +1,252 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch_otp_device.c + * @brief Implementation of the PATCH /otp-devices/$ID request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDevicePatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicePatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicePatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicePatchHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /otp-devices/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_otp_device_patch_cancel (tph); +} + + +struct TALER_MERCHANT_OtpDevicePatchHandle * +TALER_MERCHANT_otp_device_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + const char *otp_device_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm mca, + uint64_t otp_ctr, + TALER_MERCHANT_OtpDevicePatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicePatchHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("otp_device_description", + otp_device_description), + GNUNET_JSON_pack_uint64 ("otp_algorithm", + (uint32_t) mca), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_key", + otp_key)), + GNUNET_JSON_pack_uint64 ("otp_ctr", + otp_ctr)); + tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicePatchHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_patch_otp_device_finished, + tph); + } + return tph; +} + + +void +TALER_MERCHANT_otp_device_patch_cancel ( + struct TALER_MERCHANT_OtpDevicePatchHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_patch_otp_device.c */ diff --git a/src/lib/merchant_api_patch_template.c b/src/lib/merchant_api_patch_template.c index 44357cc0..7dfebf9c 100644 --- a/src/lib/merchant_api_patch_template.c +++ b/src/lib/merchant_api_patch_template.c @@ -162,7 +162,7 @@ TALER_MERCHANT_template_patch ( const char *backend_url, const char *template_id, const char *template_description, - const char *image, + const char *otp_id, json_t *template_contract, TALER_MERCHANT_TemplatePatchCallback cb, void *cb_cls) @@ -174,8 +174,8 @@ TALER_MERCHANT_template_patch ( GNUNET_JSON_pack_string ("template_description", template_description), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("image", - image)), + GNUNET_JSON_pack_string ("otp_id", + otp_id)), GNUNET_JSON_pack_object_incref ("template_contract", (json_t *) template_contract)); tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle); diff --git a/src/lib/merchant_api_post_account.c b/src/lib/merchant_api_post_account.c new file mode 100644 index 00000000..690aef17 --- /dev/null +++ b/src/lib/merchant_api_post_account.c @@ -0,0 +1,250 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post_account.c + * @brief Implementation of the POST /account request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /private/accounts operation. + */ +struct TALER_MERCHANT_AccountsPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountsPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP POST /account request. + * + * @param cls the `struct TALER_MERCHANT_AccountPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountsPostHandle *aph = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountsPostResponse apr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + aph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /accounts completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_wire", + &apr.details.ok.h_wire), + GNUNET_JSON_spec_fixed_auto ("salt", + &apr.details.ok.salt), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + apr.hr.http_status = 0; + apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + } + break; + case MHD_HTTP_BAD_REQUEST: + GNUNET_break_op (0); + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &apr.hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) apr.hr.ec); + GNUNET_break_op (0); + break; + } + aph->cb (aph->cb_cls, + &apr); + TALER_MERCHANT_accounts_post_cancel (aph); +} + + +struct TALER_MERCHANT_AccountsPostHandle * +TALER_MERCHANT_accounts_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + TALER_MERCHANT_AccountsPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountsPostHandle *aph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ( + "payto_uri", + payto_uri), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ( + "credit_facade_url", + credit_facade_url)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ( + "credit_facade_credentials", + (json_t *) credit_facade_credentials)) + ); + aph = GNUNET_new (struct TALER_MERCHANT_AccountsPostHandle); + aph->ctx = ctx; + aph->cb = cb; + aph->cb_cls = cb_cls; + aph->url = TALER_url_join (backend_url, + "private/accounts", + NULL); + if (NULL == aph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (aph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (aph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&aph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + aph->job = GNUNET_CURL_job_add2 (ctx, + eh, + aph->post_ctx.headers, + &handle_post_account_finished, + aph); + GNUNET_assert (NULL != aph->job); + } + return aph; +} + + +void +TALER_MERCHANT_accounts_post_cancel ( + struct TALER_MERCHANT_AccountsPostHandle *aph) +{ + if (NULL != aph->job) + { + GNUNET_CURL_job_cancel (aph->job); + aph->job = NULL; + } + TALER_curl_easy_post_finished (&aph->post_ctx); + GNUNET_free (aph->url); + GNUNET_free (aph); +} + + +/* end of merchant_api_post_account.c */ diff --git a/src/lib/merchant_api_post_instances.c b/src/lib/merchant_api_post_instances.c index 183b3400..73d36369 100644 --- a/src/lib/merchant_api_post_instances.c +++ b/src/lib/merchant_api_post_instances.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -32,6 +32,7 @@ #include "merchant_api_common.h" #include <taler/taler_json_lib.h> #include <taler/taler_curl_lib.h> +#include <taler/taler_kyclogic_lib.h> /** @@ -163,14 +164,11 @@ TALER_MERCHANT_instances_post ( struct GNUNET_CURL_Context *ctx, const char *backend_url, const char *instance_id, - unsigned int accounts_length, - const char *payto_uris[], const char *name, + enum TALER_KYCLOGIC_KycUserType ut, const json_t *address, const json_t *jurisdiction, - const struct TALER_Amount *default_max_wire_fee, - uint32_t default_wire_fee_amortization, - const struct TALER_Amount *default_max_deposit_fee, + bool use_stefan, struct GNUNET_TIME_Relative default_wire_transfer_delay, struct GNUNET_TIME_Relative default_pay_delay, const char *auth_token, @@ -178,10 +176,16 @@ TALER_MERCHANT_instances_post ( void *cb_cls) { struct TALER_MERCHANT_InstancesPostHandle *iph; - json_t *jpayto_uris; json_t *req_obj; json_t *auth_obj; + const char *uts; + uts = TALER_KYCLOGIC_kyc_user_type2s (ut); + if (NULL == uts) + { + GNUNET_break (0); + return NULL; + } if (NULL != auth_token) { if (0 != strncasecmp (RFC_8959_PREFIX, @@ -210,42 +214,19 @@ TALER_MERCHANT_instances_post ( GNUNET_break (0); return NULL; } - jpayto_uris = json_array (); - if (NULL == jpayto_uris) - { - json_decref (auth_obj); - GNUNET_break (0); - return NULL; - } - for (unsigned int i = 0; i<accounts_length; i++) - { - if (0 != - json_array_append_new (jpayto_uris, - json_string (payto_uris[i]))) - { - GNUNET_break (0); - json_decref (auth_obj); - json_decref (jpayto_uris); - return NULL; - } - } req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("payto_uris", - jpayto_uris), GNUNET_JSON_pack_string ("id", instance_id), GNUNET_JSON_pack_string ("name", name), + GNUNET_JSON_pack_string ("user_type", + uts), GNUNET_JSON_pack_object_incref ("address", (json_t *) address), GNUNET_JSON_pack_object_incref ("jurisdiction", (json_t *) jurisdiction), - TALER_JSON_pack_amount ("default_max_wire_fee", - default_max_wire_fee), - GNUNET_JSON_pack_uint64 ("default_wire_fee_amortization", - default_wire_fee_amortization), - TALER_JSON_pack_amount ("default_max_deposit_fee", - default_max_deposit_fee), + GNUNET_JSON_pack_bool ("use_stefan", + use_stefan), GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay", default_wire_transfer_delay), GNUNET_JSON_pack_time_rel ("default_pay_delay", diff --git a/src/lib/merchant_api_post_order_abort.c b/src/lib/merchant_api_post_order_abort.c index b5a7a00b..270ceb7e 100644 --- a/src/lib/merchant_api_post_order_abort.c +++ b/src/lib/merchant_api_post_order_abort.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -39,6 +39,12 @@ /** + * Maximum number of refunds we return. + */ +#define MAX_REFUNDS 1024 + + +/** * @brief An abort Handle */ struct TALER_MERCHANT_OrderAbortHandle @@ -102,17 +108,20 @@ struct TALER_MERCHANT_OrderAbortHandle * OK. Otherwise returns #GNUNET_SYSERR. * * @param oah handle to operation that created the reply + * @param[in] ar abort response, partially initialized * @param json the reply to parse * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah, + struct TALER_MERCHANT_AbortResponse *ar, const json_t *json) { - json_t *refunds; + const json_t *refunds; unsigned int num_refunds; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("refunds", &refunds), + GNUNET_JSON_spec_array_const ("refunds", + &refunds), GNUNET_JSON_spec_end () }; @@ -124,13 +133,14 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah, GNUNET_break_op (0); return GNUNET_SYSERR; } - if (! json_is_array (refunds)) + num_refunds = (unsigned int) json_array_size (refunds); + if ( (json_array_size (refunds) != (size_t) num_refunds) || + (num_refunds > MAX_REFUNDS) ) { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); + GNUNET_break (0); return GNUNET_SYSERR; } - num_refunds = json_array_size (refunds); + { struct TALER_MERCHANT_AbortedCoin res[GNUNET_NZL (num_refunds)]; @@ -150,7 +160,6 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah, NULL, NULL)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } if (MHD_HTTP_OK == exchange_status) @@ -169,7 +178,6 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah, NULL, NULL)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } @@ -184,26 +192,17 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah, &res[i].exchange_sig)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } } } - { - struct TALER_MERCHANT_HttpResponse hr = { - .reply = json, - .http_status = MHD_HTTP_OK - }; - - oah->abort_cb (oah->abort_cb_cls, - &hr, - &oah->merchant_pub, - num_refunds, - res); - } + ar->details.ok.merchant_pub = &oah->merchant_pub; + ar->details.ok.num_aborts = num_refunds; + ar->details.ok.aborts = res; + oah->abort_cb (oah->abort_cb_cls, + ar); oah->abort_cb = NULL; } - GNUNET_JSON_parse_free (spec); return GNUNET_OK; } @@ -223,9 +222,9 @@ handle_abort_finished (void *cls, { struct TALER_MERCHANT_OrderAbortHandle *oah = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_AbortResponse ar = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; oah->job = NULL; @@ -235,40 +234,41 @@ handle_abort_finished (void *cls, switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: if (GNUNET_OK == check_abort_refund (oah, + &ar, json)) { TALER_MERCHANT_order_abort_cancel (oah); return; } - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + ar.hr.http_status = 0; + ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); /* This should never happen, either us or the merchant is buggy (or API version conflict); just pass JSON reply to the application */ break; case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); break; case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ break; case MHD_HTTP_REQUEST_TIMEOUT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says one of the signatures is invalid; as we checked them, this should never happen, we should pass the JSON @@ -276,19 +276,19 @@ handle_abort_finished (void *cls, break; case MHD_HTTP_PRECONDITION_FAILED: /* Our *payment* already succeeded fully. */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); /* Server had an internal issue; we should retry, but this API leaves this to the application */ break; case MHD_HTTP_BAD_GATEWAY: TALER_MERCHANT_parse_error_details_ (json, response_code, - &hr); + &ar.hr); /* Nothing really to verify, the merchant is blaming the exchange. We should pass the JSON reply to the application */ break; @@ -296,33 +296,31 @@ handle_abort_finished (void *cls, /* unexpected response code */ TALER_MERCHANT_parse_error_details_ (json, response_code, - &hr); + &ar.hr); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) ar.hr.ec); GNUNET_break_op (0); break; } oah->abort_cb (oah->abort_cb_cls, - &hr, - NULL, - 0, - NULL); + &ar); TALER_MERCHANT_order_abort_cancel (oah); } struct TALER_MERCHANT_OrderAbortHandle * -TALER_MERCHANT_order_abort (struct GNUNET_CURL_Context *ctx, - const char *merchant_url, - const char *order_id, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_PrivateContractHashP *h_contract, - unsigned int num_coins, - const struct TALER_MERCHANT_AbortCoin coins[], - TALER_MERCHANT_AbortCallback cb, - void *cb_cls) +TALER_MERCHANT_order_abort ( + struct GNUNET_CURL_Context *ctx, + const char *merchant_url, + const char *order_id, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_PrivateContractHashP *h_contract, + unsigned int num_coins, + const struct TALER_MERCHANT_AbortCoin coins[static num_coins], + TALER_MERCHANT_AbortCallback cb, + void *cb_cls) { struct TALER_MERCHANT_OrderAbortHandle *oah; json_t *abort_obj; @@ -389,9 +387,9 @@ TALER_MERCHANT_order_abort (struct GNUNET_CURL_Context *ctx, oah->num_coins = num_coins; oah->coins = GNUNET_new_array (num_coins, struct TALER_MERCHANT_AbortCoin); - memcpy (oah->coins, - coins, - num_coins * sizeof (struct TALER_MERCHANT_AbortCoin)); + GNUNET_memcpy (oah->coins, + coins, + num_coins * sizeof (struct TALER_MERCHANT_AbortCoin)); { CURL *eh; diff --git a/src/lib/merchant_api_post_order_claim.c b/src/lib/merchant_api_post_order_claim.c index 66e58c2a..76802ea5 100644 --- a/src/lib/merchant_api_post_order_claim.c +++ b/src/lib/merchant_api_post_order_claim.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -86,40 +86,36 @@ handle_post_order_claim_finished (void *cls, const void *response) { struct TALER_MERCHANT_OrderClaimHandle *och = cls; - json_t *contract_terms; - struct TALER_MerchantSignatureP sig; - struct TALER_PrivateContractHashP hash; const json_t *json = response; + struct TALER_MERCHANT_OrderClaimResponse ocr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("contract_terms", - &contract_terms), - GNUNET_JSON_spec_fixed_auto ("sig", - &sig), + GNUNET_JSON_spec_object_const ( + "contract_terms", + &ocr.details.ok.contract_terms), + GNUNET_JSON_spec_fixed_auto ( + "sig", + &ocr.details.ok.sig), GNUNET_JSON_spec_end () }; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; och->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Order claimed with status %u\n", (unsigned int) response_code); if (MHD_HTTP_OK != response_code) { - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ocr.hr.ec = TALER_JSON_get_error_code (json); + ocr.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Proposal lookup failed with HTTP status code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) ocr.hr.ec); och->cb (och->cb_cls, - &hr, - NULL, - NULL, - NULL); + &ocr); TALER_MERCHANT_order_claim_cancel (och); return; } @@ -132,39 +128,29 @@ handle_post_order_claim_finished (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Claiming order failed: could not parse JSON response\n"); GNUNET_break_op (0); - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - hr.http_status = 0; + ocr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + ocr.hr.http_status = 0; och->cb (och->cb_cls, - &hr, - NULL, - NULL, - NULL); + &ocr); TALER_MERCHANT_order_claim_cancel (och); return; } if (GNUNET_OK != - TALER_JSON_contract_hash (contract_terms, - &hash)) + TALER_JSON_contract_hash (ocr.details.ok.contract_terms, + &ocr.details.ok.h_contract_terms)) { GNUNET_break (0); - hr.ec = TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE; - hr.http_status = 0; + ocr.hr.ec = TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE; + ocr.hr.http_status = 0; GNUNET_JSON_parse_free (spec); och->cb (och->cb_cls, - &hr, - NULL, - NULL, - NULL); + &ocr); TALER_MERCHANT_order_claim_cancel (och); return; } - och->cb (och->cb_cls, - &hr, - contract_terms, - &sig, - &hash); + &ocr); GNUNET_JSON_parse_free (spec); TALER_MERCHANT_order_claim_cancel (och); } diff --git a/src/lib/merchant_api_post_order_paid.c b/src/lib/merchant_api_post_order_paid.c index a42b1255..785d956f 100644 --- a/src/lib/merchant_api_post_order_paid.c +++ b/src/lib/merchant_api_post_order_paid.c @@ -90,9 +90,9 @@ handle_paid_finished (void *cls, { struct TALER_MERCHANT_OrderPaidHandle *oph = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_OrderPaidResponse opr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; oph->job = NULL; @@ -102,61 +102,81 @@ handle_paid_finished (void *cls, switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; - case MHD_HTTP_NO_CONTENT: + case MHD_HTTP_OK: + { + bool refunded; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_bool ("refunded", + &refunded), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (opr.hr.reply, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + break; + } break; case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); /* This should never happen, either us * or the merchant is buggy (or API version conflict); * just pass JSON reply to the application */ break; case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); /* The signature provided was invalid */ break; case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ break; case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); /* The hashed contract terms don't match with the order_id. */ break; case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); /* Server had an internal issue; we should retry, but this API leaves this to the application */ break; case MHD_HTTP_SERVICE_UNAVAILABLE: TALER_MERCHANT_parse_error_details_ (json, response_code, - &hr); + &opr.hr); /* Exchange couldn't respond properly; the retry is left to the application */ break; default: TALER_MERCHANT_parse_error_details_ (json, response_code, - &hr); + &opr.hr); /* unexpected response code */ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) opr.hr.ec); GNUNET_break_op (0); break; } oph->paid_cb (oph->paid_cb_cls, - &hr); + &opr); TALER_MERCHANT_order_paid_cancel (oph); } @@ -168,6 +188,7 @@ TALER_MERCHANT_order_paid ( const char *order_id, const char *session_id, const struct TALER_PrivateContractHashP *h_contract_terms, + const struct GNUNET_HashCode *wallet_data_hash, const struct TALER_MerchantSignatureP *merchant_sig, TALER_MERCHANT_OrderPaidCallback paid_cb, void *paid_cb_cls) @@ -180,6 +201,9 @@ TALER_MERCHANT_order_paid ( merchant_sig), GNUNET_JSON_pack_data_auto ("h_contract", h_contract_terms), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_data_auto ("wallet_data_hash", + wallet_data_hash)), GNUNET_JSON_pack_string ("session_id", session_id)); oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle); diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c index 765ca1a6..57c85565 100644 --- a/src/lib/merchant_api_post_order_pay.c +++ b/src/lib/merchant_api_post_order_pay.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -109,12 +109,6 @@ struct TALER_MERCHANT_OrderPayHandle json_t *error_history; /** - * Handle to the exchange that issued a problematic - * coin (if any). - */ - struct TALER_EXCHANGE_Handle *exchange; - - /** * Number of @e coins we are paying with. */ unsigned int num_coins; @@ -129,248 +123,6 @@ struct TALER_MERCHANT_OrderPayHandle /** - * We got a 409 response back from the exchange (or the merchant). - * Now we need to check the provided cryptographic proof that the - * coin was actually already spent! - * - * @param oph operation handle - * @param keys key data from the exchange - * @return #GNUNET_OK if conflict is valid - */ -static enum GNUNET_GenericReturnValue -check_conflict (struct TALER_MERCHANT_OrderPayHandle *oph, - const struct TALER_EXCHANGE_Keys *keys) -{ - struct TALER_Amount spent; - struct TALER_Amount spent_plus_contrib; - struct TALER_DenominationHashP h_denom_pub_pc; - const struct TALER_EXCHANGE_DenomPublicKey *dpk; - - TALER_denom_pub_hash (&oph->error_pc->denom_pub, - &h_denom_pub_pc); - dpk = TALER_EXCHANGE_get_denomination_key_by_hash ( - keys, - &h_denom_pub_pc); - if (GNUNET_OK != - TALER_EXCHANGE_verify_coin_history (dpk, - &oph->error_pc->coin_pub, - oph->error_history, - &spent)) - { - /* Exchange's history fails to verify */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 > - TALER_amount_add (&spent_plus_contrib, - &spent, - &oph->error_pc->amount_with_fee)) - { - /* We got an integer overflow? Bad application! */ - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (-1 != TALER_amount_cmp (&oph->error_pc->denom_value, - &spent_plus_contrib)) - { - /* according to our calculations, the transaction should - have still worked, AND we did not get any proof of - coin public key re-use; hence: exchange error! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Accepting proof of double-spending (or coin public key re-use)\n"); - return GNUNET_OK; -} - - -/** - * We got the fee structure from the exchange. Now - * validate the conflict error. - * - * @param cls a `struct TALER_MERCHANT_OrderPayHandle` - * @param ehr reply from the exchange - * @param keys the key structure - * @param compat protocol compatibility indication - */ -static void -cert_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *ehr, - const struct TALER_EXCHANGE_Keys *keys, - enum TALER_EXCHANGE_VersionCompatibility compat) -{ - struct TALER_MERCHANT_OrderPayHandle *oph = cls; - - if (TALER_EXCHANGE_VC_INCOMPATIBLE & compat) - { - struct TALER_MERCHANT_PayResponse pr = { - .hr.http_status = MHD_HTTP_CONFLICT, - .hr.exchange_http_status = 0, - .hr.ec = TALER_EC_WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE, - .hr.reply = oph->full_reply, - .hr.exchange_reply = ehr->reply, - .hr.hint = "could not check error: incompatible exchange version" - }; - - oph->pay_cb (oph->pay_cb_cls, - &pr); - TALER_MERCHANT_order_pay_cancel (oph); - return; - } - if ( (MHD_HTTP_OK != ehr->http_status) || - (NULL == keys) ) - { - struct TALER_MERCHANT_PayResponse pr = { - .hr.http_status = MHD_HTTP_CONFLICT, - .hr.exchange_http_status = ehr->http_status, - .hr.ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - .hr.reply = oph->full_reply, - .hr.exchange_reply = ehr->reply, - .hr.hint = "failed to download /keys from the exchange" - }; - - oph->pay_cb (oph->pay_cb_cls, - &pr); - TALER_MERCHANT_order_pay_cancel (oph); - return; - } - - if (GNUNET_OK != - check_conflict (oph, - keys)) - { - struct TALER_MERCHANT_PayResponse pr = { - .hr.http_status = 0, - .hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE, - .hr.reply = oph->full_reply - }; - - oph->pay_cb (oph->pay_cb_cls, - &pr); - TALER_MERCHANT_order_pay_cancel (oph); - return; - } - - { - struct TALER_MERCHANT_PayResponse pr = { - .hr.http_status = MHD_HTTP_CONFLICT, - .hr.ec = TALER_JSON_get_error_code (oph->full_reply), - .hr.reply = oph->full_reply - }; - - oph->pay_cb (oph->pay_cb_cls, - &pr); - TALER_MERCHANT_order_pay_cancel (oph); - } -} - - -/** - * We got a 409 response back from the exchange (or the merchant). - * Now we need to check the provided cryptograophic proof that the - * coin was actually already spent! - * - * @param[in,out] oph handle of the original pay operation - * @param[in,out] pr response to modify if #GNUNET_OK is returned - * @param json cryptograophic proof returned by the - * exchange/merchant - * @return #GNUNET_OK if proof checks out, - * #GNUNET_SYSERR if it is wrong, - * #GNUNET_NO if checking continues asynchronously - */ -static enum GNUNET_GenericReturnValue -parse_conflict (struct TALER_MERCHANT_OrderPayHandle *oph, - struct TALER_MERCHANT_PayResponse *pr, - const json_t *json) -{ - json_t *ereply; - const char *exchange_url; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("exchange_reply", - &ereply), - GNUNET_JSON_spec_string ("exchange_url", - &exchange_url), - GNUNET_JSON_spec_end () - }; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct GNUNET_JSON_Specification hspec[] = { - GNUNET_JSON_spec_json ("history", - &oph->error_history), - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &coin_pub), - GNUNET_JSON_spec_end () - }; - enum TALER_ErrorCode ec = TALER_JSON_get_error_code (json); - - switch (ec) - { - case TALER_EC_GENERIC_CURRENCY_MISMATCH: - /* no proof to check, still very strange, as we - should have checked that the currency matches */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - MHD_HTTP_CONFLICT, - &pr->hr); - return GNUNET_OK; - case TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_ALREADY_PAID: - /* We can only be happy and accept the result; - FIXME: parse the refunds... */ - TALER_MERCHANT_parse_error_details_ (json, - MHD_HTTP_CONFLICT, - &pr->hr); - return GNUNET_OK; - case TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS: - /* main case, handled below */ - break; - default: - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_JSON_parse (ereply, - hspec, - NULL, NULL)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - GNUNET_JSON_parse_free (spec); - - for (unsigned int i = 0; i<oph->num_coins; i++) - { - if (0 == - GNUNET_memcmp (&oph->coins[i].coin_pub, - &coin_pub)) - { - oph->error_pc = &oph->coins[i]; - oph->full_reply = json_incref ((json_t *) json); - oph->exchange = TALER_EXCHANGE_connect (oph->ctx, - oph->error_pc->exchange_url, - &cert_cb, - oph, - TALER_EXCHANGE_OPTION_END); - return GNUNET_NO; - } - } - GNUNET_break_op (0); /* complaint is not about any of the coins - that we actually paid with... */ - GNUNET_JSON_parse_free (hspec); - return GNUNET_SYSERR; -} - - -/** * Function called when we're done processing the * HTTP /pay request. * @@ -406,7 +158,12 @@ handle_pay_finished (void *cls, struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ( "sig", - &pr.details.success.merchant_sig), + &pr.details.ok.merchant_sig), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ( + "pos_confirmation", + &pr.details.ok.pos_confirmation), + NULL), GNUNET_JSON_spec_end () }; @@ -425,7 +182,7 @@ handle_pay_finished (void *cls, if (GNUNET_OK != TALER_merchant_pay_verify (&oph->h_contract_terms, &oph->merchant_pub, - &pr.details.success.merchant_sig)) + &pr.details.ok.merchant_sig)) { GNUNET_break_op (0); pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; @@ -475,27 +232,10 @@ handle_pay_finished (void *cls, Pass on to application. */ break; case MHD_HTTP_CONFLICT: - { - enum GNUNET_GenericReturnValue ret; - - ret = parse_conflict (oph, - &pr, - json); - switch (ret) - { - case GNUNET_OK: - /* continued below, 'pr' was modified */ - break; - case GNUNET_NO: - /* handled asynchronously! */ - return; /* ! */ - case GNUNET_SYSERR: - GNUNET_break_op (0); - response_code = 0; - break; - } - break; - } + TALER_MERCHANT_parse_error_details_ (json, + MHD_HTTP_CONFLICT, + &pr.hr); + break; case MHD_HTTP_GONE: TALER_MERCHANT_parse_error_details_ (json, response_code, @@ -565,8 +305,9 @@ TALER_MERCHANT_order_pay_frontend ( const char *merchant_url, const char *order_id, const char *session_id, + const json_t *wallet_data, unsigned int num_coins, - const struct TALER_MERCHANT_PaidCoin coins[], + const struct TALER_MERCHANT_PaidCoin coins[static num_coins], TALER_MERCHANT_OrderPayCallback pay_cb, void *pay_cb_cls) { @@ -655,6 +396,9 @@ TALER_MERCHANT_order_pay_frontend ( GNUNET_JSON_pack_array_steal ("coins", j_coins), GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("wallet_data", + (json_t *) wallet_data)), + GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("session_id", session_id))); @@ -684,9 +428,9 @@ TALER_MERCHANT_order_pay_frontend ( oph->num_coins = num_coins; oph->coins = GNUNET_new_array (num_coins, struct TALER_MERCHANT_PaidCoin); - memcpy (oph->coins, - coins, - num_coins * sizeof (struct TALER_MERCHANT_PaidCoin)); + GNUNET_memcpy (oph->coins, + coins, + num_coins * sizeof (struct TALER_MERCHANT_PaidCoin)); eh = TALER_MERCHANT_curl_easy_get_ (oph->url); if (GNUNET_OK != @@ -717,6 +461,7 @@ TALER_MERCHANT_order_pay ( const char *merchant_url, const char *session_id, const struct TALER_PrivateContractHashP *h_contract_terms, + const json_t *wallet_data, const struct TALER_Amount *amount, const struct TALER_Amount *max_fee, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -727,10 +472,12 @@ TALER_MERCHANT_order_pay ( const struct TALER_MerchantWireHashP *h_wire, const char *order_id, unsigned int num_coins, - const struct TALER_MERCHANT_PayCoin coins[], + const struct TALER_MERCHANT_PayCoin coins[static num_coins], TALER_MERCHANT_OrderPayCallback pay_cb, void *pay_cb_cls) { + struct GNUNET_HashCode wallet_data_hash; + if (GNUNET_YES != TALER_amount_cmp_currency (amount, max_fee)) @@ -738,7 +485,9 @@ TALER_MERCHANT_order_pay ( GNUNET_break (0); return NULL; } - + if (NULL != wallet_data) + TALER_json_hash (wallet_data, + &wallet_data_hash); { struct TALER_MERCHANT_PaidCoin pc[num_coins]; @@ -765,6 +514,9 @@ TALER_MERCHANT_order_pay ( &fee, h_wire, h_contract_terms, + (NULL != wallet_data) + ? &wallet_data_hash + : NULL, coin->h_age_commitment, NULL /* h_extensions! */, &h_denom_pub, @@ -789,6 +541,7 @@ TALER_MERCHANT_order_pay ( merchant_url, order_id, session_id, + wallet_data, num_coins, pc, pay_cb, @@ -812,11 +565,6 @@ TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph) GNUNET_CURL_job_cancel (oph->job); oph->job = NULL; } - if (NULL != oph->exchange) - { - TALER_EXCHANGE_disconnect (oph->exchange); - oph->exchange = NULL; - } TALER_curl_easy_post_finished (&oph->post_ctx); json_decref (oph->error_history); json_decref (oph->full_reply); diff --git a/src/lib/merchant_api_post_order_refund.c b/src/lib/merchant_api_post_order_refund.c index be996dc2..4414bd86 100644 --- a/src/lib/merchant_api_post_order_refund.c +++ b/src/lib/merchant_api_post_order_refund.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -85,30 +85,26 @@ handle_refund_finished (void *cls, { struct TALER_MERCHANT_OrderRefundHandle *orh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_RefundResponse rr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; orh->job = NULL; switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL); + rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: { - const char *taler_refund_uri; - struct TALER_PrivateContractHashP h_contract; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("taler_refund_uri", - &taler_refund_uri), - GNUNET_JSON_spec_fixed_auto ("h_contract", - &h_contract), + GNUNET_JSON_spec_string ( + "taler_refund_uri", + &rr.details.ok.taler_refund_uri), + GNUNET_JSON_spec_fixed_auto ( + "h_contract", + &rr.details.ok.h_contract), GNUNET_JSON_spec_end () }; @@ -118,51 +114,36 @@ handle_refund_finished (void *cls, NULL, NULL)) { GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL); + rr.hr.http_status = 0; + rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } - orh->cb (orh->cb_cls, - &hr, - taler_refund_uri, - &h_contract); - GNUNET_JSON_parse_free (spec); + break; } - break; case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + rr.hr.ec = TALER_JSON_get_error_code (json); + rr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + rr.hr.ec = TALER_JSON_get_error_code (json); + rr.hr.hint = TALER_JSON_get_error_hint (json); /* Nothing really to verify, merchant says we need to authenticate. */ break; case MHD_HTTP_NOT_FOUND: case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL); + rr.hr.ec = TALER_JSON_get_error_code (json); + rr.hr.hint = TALER_JSON_get_error_hint (json); break; default: GNUNET_break_op (0); /* unexpected status code */ TALER_MERCHANT_parse_error_details_ (json, response_code, - &hr); - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL); + &rr.hr); break; } + orh->cb (orh->cb_cls, + &rr); TALER_MERCHANT_post_order_refund_cancel (orh); } diff --git a/src/lib/merchant_api_post_orders.c b/src/lib/merchant_api_post_orders.c index c0fd5fcb..56881133 100644 --- a/src/lib/merchant_api_post_orders.c +++ b/src/lib/merchant_api_post_orders.c @@ -92,9 +92,9 @@ handle_post_order_finished (void *cls, po->job = NULL; TALER_MERCHANT_handle_order_creation_response_ (po->cb, - po->cb_cls, - response_code, - json); + po->cb_cls, + response_code, + json); TALER_MERCHANT_orders_post_cancel (po); } @@ -107,6 +107,8 @@ TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx, TALER_MERCHANT_PostOrdersCallback cb, void *cb_cls) { + static const char *no_uuids[GNUNET_NZL (0)]; + return TALER_MERCHANT_orders_post2 (ctx, backend_url, order, @@ -115,7 +117,7 @@ TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx, 0, NULL, 0, - NULL, + no_uuids, true, cb, cb_cls); @@ -132,7 +134,40 @@ TALER_MERCHANT_orders_post2 ( unsigned int inventory_products_length, const struct TALER_MERCHANT_InventoryProduct inventory_products[], unsigned int uuids_length, - const char *uuids[], + const char *uuids[static uuids_length], + bool create_token, + TALER_MERCHANT_PostOrdersCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_orders_post3 ( + ctx, + backend_url, + order, + NULL, /* session ID */ + refund_delay, + payment_target, + inventory_products_length, + inventory_products, + uuids_length, + uuids, + create_token, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_PostOrdersHandle * +TALER_MERCHANT_orders_post3 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const json_t *order, + const char *session_id, + struct GNUNET_TIME_Relative refund_delay, + const char *payment_target, + unsigned int inventory_products_length, + const struct TALER_MERCHANT_InventoryProduct inventory_products[], + unsigned int uuids_length, + const char *uuids[static uuids_length], bool create_token, TALER_MERCHANT_PostOrdersCallback cb, void *cb_cls) @@ -152,6 +187,9 @@ TALER_MERCHANT_orders_post2 ( GNUNET_JSON_pack_object_incref ("order", (json_t *) order), GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("session_id", + session_id)), + GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("payment_target", payment_target))); if (0 != refund_delay.rel_value_us) diff --git a/src/lib/merchant_api_post_otp_devices.c b/src/lib/merchant_api_post_otp_devices.c new file mode 100644 index 00000000..456abd09 --- /dev/null +++ b/src/lib/merchant_api_post_otp_devices.c @@ -0,0 +1,237 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post_otp_devices.c + * @brief Implementation of the POST /otp-devices request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDevicesPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicesPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /otp-devices request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicesPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_otp_devices_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicesPostHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /otp-devices completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_otp_devices_post_cancel (tph); +} + + +struct TALER_MERCHANT_OtpDevicesPostHandle * +TALER_MERCHANT_otp_devices_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + const char *otp_device_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm otp_algorithm, + uint64_t otp_ctr, + TALER_MERCHANT_OtpDevicesPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicesPostHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("otp_device_id", + otp_device_id), + GNUNET_JSON_pack_string ("otp_device_description", + otp_device_description), + GNUNET_JSON_pack_uint64 ("otp_algorithm", + (uint32_t) otp_algorithm), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_key", + otp_key)), + GNUNET_JSON_pack_uint64 ("otp_ctr", + otp_ctr)); + tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicesPostHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + tph->url = TALER_url_join (backend_url, + "private/otp-devices", + NULL); + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_post_otp_devices_finished, + tph); + GNUNET_assert (NULL != tph->job); + } + return tph; +} + + +void +TALER_MERCHANT_otp_devices_post_cancel ( + struct TALER_MERCHANT_OtpDevicesPostHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_post_otp_devices.c */ diff --git a/src/lib/merchant_api_post_products.c b/src/lib/merchant_api_post_products.c index 2a5b9a9b..0f09f397 100644 --- a/src/lib/merchant_api_post_products.c +++ b/src/lib/merchant_api_post_products.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020-2021 Taler Systems SA + Copyright (C) 2020-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -159,7 +159,7 @@ handle_post_products_finished (void *cls, struct TALER_MERCHANT_ProductsPostHandle * -TALER_MERCHANT_products_post ( +TALER_MERCHANT_products_post2 ( struct GNUNET_CURL_Context *ctx, const char *backend_url, const char *product_id, @@ -172,6 +172,7 @@ TALER_MERCHANT_products_post ( int64_t total_stock, const json_t *address, struct GNUNET_TIME_Timestamp next_restock, + uint32_t minimum_age, TALER_MERCHANT_ProductsPostCallback cb, void *cb_cls) { @@ -183,20 +184,26 @@ TALER_MERCHANT_products_post ( product_id), GNUNET_JSON_pack_string ("description", description), - GNUNET_JSON_pack_object_incref ("description_i18n", - (json_t *) description_i18n), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("description_i18n", + (json_t *) description_i18n)), GNUNET_JSON_pack_string ("unit", unit), TALER_JSON_pack_amount ("price", price), GNUNET_JSON_pack_string ("image", image), - GNUNET_JSON_pack_array_incref ("taxes", - (json_t *) taxes), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_incref ("taxes", + (json_t *) taxes)), GNUNET_JSON_pack_uint64 ("total_stock", total_stock), - GNUNET_JSON_pack_object_incref ("address", - (json_t *) address), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_uint64 ("minimum_age", + minimum_age)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("address", + (json_t *) address)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_timestamp ("next_restock", next_restock))); @@ -235,6 +242,41 @@ TALER_MERCHANT_products_post ( } +struct TALER_MERCHANT_ProductsPostHandle * +TALER_MERCHANT_products_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *description, + const json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + const char *image, + const json_t *taxes, + int64_t total_stock, + const json_t *address, + struct GNUNET_TIME_Timestamp next_restock, + TALER_MERCHANT_ProductsPostCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_products_post2 (ctx, + backend_url, + product_id, + description, + description_i18n, + unit, + price, + image, + taxes, + total_stock, + address, + next_restock, + 0, + cb, + cb_cls); +} + + void TALER_MERCHANT_products_post_cancel ( struct TALER_MERCHANT_ProductsPostHandle *pph) diff --git a/src/lib/merchant_api_post_reserves.c b/src/lib/merchant_api_post_reserves.c deleted file mode 100644 index 65239dfb..00000000 --- a/src/lib/merchant_api_post_reserves.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015, 2016, 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_reserves.c - * @brief Implementation of the POST /reserves request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_curl_lib.h> -#include <taler/taler_json_lib.h> - - -/** - * @brief A handle for POSTing reserve data. - */ -struct TALER_MERCHANT_PostReservesHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_PostReservesCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP POST /reserves request. - * - * @param cls the `struct TALER_MERCHANT_PostReservesHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_reserves_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_PostReservesHandle *prh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - prh->job = NULL; - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - const char *payto_uri; - struct TALER_ReservePublicKeyP reserve_pub; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("reserve_pub", - &reserve_pub), - GNUNET_JSON_spec_string ("payto_uri", - &payto_uri), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - else - { - prh->cb (prh->cb_cls, - &hr, - &reserve_pub, - payto_uri); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_reserves_post_cancel (prh); - return; - } - } - case MHD_HTTP_ACCEPTED: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Did not find any data\n"); - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - prh->cb (prh->cb_cls, - &hr, - NULL, - NULL); - TALER_MERCHANT_reserves_post_cancel (prh); -} - - -struct TALER_MERCHANT_PostReservesHandle * -TALER_MERCHANT_reserves_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_Amount *initial_balance, - const char *exchange_url, - const char *wire_method, - TALER_MERCHANT_PostReservesCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_PostReservesHandle *prh; - CURL *eh; - json_t *req; - - prh = GNUNET_new (struct TALER_MERCHANT_PostReservesHandle); - prh->ctx = ctx; - prh->cb = cb; - prh->cb_cls = cb_cls; - prh->url = TALER_url_join (backend_url, - "private/reserves", - NULL); - if (NULL == prh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (prh); - return NULL; - } - req = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("initial_balance", - initial_balance), - GNUNET_JSON_pack_string ("wire_method", - wire_method), - GNUNET_JSON_pack_string ("exchange_url", - exchange_url)); - eh = TALER_MERCHANT_curl_easy_get_ (prh->url); - if (GNUNET_OK != - TALER_curl_easy_post (&prh->post_ctx, - eh, - req)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req); - GNUNET_free (prh->url); - GNUNET_free (prh); - return NULL; - } - json_decref (req); - prh->job = GNUNET_CURL_job_add2 (ctx, - eh, - prh->post_ctx.headers, - &handle_post_reserves_finished, - prh); - return prh; -} - - -void -TALER_MERCHANT_reserves_post_cancel ( - struct TALER_MERCHANT_PostReservesHandle *prh) -{ - if (NULL != prh->job) - { - GNUNET_CURL_job_cancel (prh->job); - prh->job = NULL; - } - GNUNET_free (prh->url); - TALER_curl_easy_post_finished (&prh->post_ctx); - GNUNET_free (prh); -} - - -/* end of merchant_api_post_reserves.c */ diff --git a/src/lib/merchant_api_post_templates.c b/src/lib/merchant_api_post_templates.c index 9592a5e3..3ab4320c 100644 --- a/src/lib/merchant_api_post_templates.c +++ b/src/lib/merchant_api_post_templates.c @@ -129,6 +129,10 @@ handle_post_templates_finished (void *cls, happen, we should pass the JSON reply to the application */ break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; case MHD_HTTP_INTERNAL_SERVER_ERROR: hr.ec = TALER_JSON_get_error_code (json); hr.hint = TALER_JSON_get_error_hint (json); @@ -199,7 +203,7 @@ TALER_MERCHANT_templates_post ( const char *backend_url, const char *template_id, const char *template_description, - const char *image, + const char *otp_id, const json_t *template_contract, TALER_MERCHANT_TemplatesPostCallback cb, void *cb_cls) @@ -218,8 +222,8 @@ TALER_MERCHANT_templates_post ( GNUNET_JSON_pack_string ("template_description", template_description), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("image", - image)), + GNUNET_JSON_pack_string ("otp_id", + otp_id)), GNUNET_JSON_pack_object_incref ("template_contract", (json_t *) template_contract)); tph = GNUNET_new (struct TALER_MERCHANT_TemplatesPostHandle); diff --git a/src/lib/merchant_api_post_tokenfamilies.c b/src/lib/merchant_api_post_tokenfamilies.c new file mode 100644 index 00000000..0c5e18c2 --- /dev/null +++ b/src/lib/merchant_api_post_tokenfamilies.c @@ -0,0 +1,246 @@ +/* + This file is part of TALER + Copyright (C) 2020-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post_tokenfamilies.c + * @brief Implementation of the POST /tokenfamilies request + * of the merchant's HTTP API + * @author Christian Blättler + */ +#include "platform.h" +#include <curl/curl.h> +#include <gnunet/gnunet_json_lib.h> +#include <gnunet/gnunet_time_lib.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /tokenfamilies operation. + */ +struct TALER_MERCHANT_TokenFamiliesPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TokenFamiliesPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + +/** + * Function called when we're done processing the + * HTTP POST /tokenfamilies request. + * + * @param cls the `struct TALER_MERCHANT_TokenFamiliesPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_token_families_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TokenFamiliesPostHandle *handle = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + handle->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /tokenfamilies completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + handle->cb (handle->cb_cls, + &hr); + TALER_MERCHANT_token_families_post_cancel (handle); +} + +struct TALER_MERCHANT_TokenFamiliesPostHandle * +TALER_MERCHANT_token_families_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *slug, + const char *name, + const char *description, + const json_t *description_i18n, + struct GNUNET_TIME_Timestamp valid_after, + struct GNUNET_TIME_Timestamp valid_before, + struct GNUNET_TIME_Relative duration, + const char *kind, + TALER_MERCHANT_TokenFamiliesPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TokenFamiliesPostHandle *handle; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("slug", + slug), + GNUNET_JSON_pack_string ("name", + name), + GNUNET_JSON_pack_string ("description", + description), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("description_i18n", + (json_t *) description_i18n)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_timestamp ("valid_after", + valid_after)), + GNUNET_JSON_pack_timestamp ("valid_before", + valid_before), + GNUNET_JSON_pack_time_rel ("duration", + duration), + GNUNET_JSON_pack_string ("kind", + kind)); + handle = GNUNET_new (struct TALER_MERCHANT_TokenFamiliesPostHandle); + handle->ctx = ctx; + handle->cb = cb; + handle->cb_cls = cb_cls; + handle->url = TALER_url_join (backend_url, + "private/tokenfamilies", + NULL); + if (NULL == handle->url) + { + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (handle); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (handle->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&handle->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + handle->job = GNUNET_CURL_job_add2 (ctx, + eh, + handle->post_ctx.headers, + &handle_post_token_families_finished, + handle); + GNUNET_assert (NULL != handle->job); + } + return handle; +} + + +void +TALER_MERCHANT_token_families_post_cancel ( + struct TALER_MERCHANT_TokenFamiliesPostHandle *pph) +{ + if (NULL != pph->job) + { + GNUNET_CURL_job_cancel (pph->job); + pph->job = NULL; + } + TALER_curl_easy_post_finished (&pph->post_ctx); + GNUNET_free (pph->url); + GNUNET_free (pph); +} diff --git a/src/lib/merchant_api_post_transfers.c b/src/lib/merchant_api_post_transfers.c index 450b46d9..615453fa 100644 --- a/src/lib/merchant_api_post_transfers.c +++ b/src/lib/merchant_api_post_transfers.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -85,116 +85,22 @@ handle_post_transfers_finished (void *cls, const void *response) { struct TALER_MERCHANT_PostTransfersHandle *pth = cls; - const json_t *json = response; - json_t *deposit_sum = NULL; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_PostTransfersResponse ptr = { + .hr.reply = response, + .hr.http_status = (unsigned int) response_code }; pth->job = NULL; switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; - case MHD_HTTP_OK: - { - struct TALER_Amount total; - struct TALER_Amount wire_fee; - struct GNUNET_TIME_Timestamp execution_time; - json_t *deposit_sums; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ("total", - &total), - TALER_JSON_spec_amount_any ("wire_fee", - &wire_fee), - GNUNET_JSON_spec_timestamp ("execution_time", - &execution_time), - GNUNET_JSON_spec_json ("deposit_sums", - &deposit_sums), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - else - { - size_t deposit_sums_length; - struct TALER_MERCHANT_TrackTransferDetail *details; - unsigned int i; - bool ok; - - if (! json_is_array (deposit_sums)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - deposit_sums_length = json_array_size (deposit_sums); - details = GNUNET_new_array (deposit_sums_length, - struct TALER_MERCHANT_TrackTransferDetail); - ok = true; - json_array_foreach (deposit_sums, i, deposit_sum) { - struct TALER_MERCHANT_TrackTransferDetail *d = &details[i]; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("order_id", - &d->order_id), - TALER_JSON_spec_amount_any ("deposit_value", - &d->deposit_value), - TALER_JSON_spec_amount_any ("deposit_fee", - &d->deposit_fee), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (deposit_sum, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - ok = false; - break; - } - } - - if (! ok) - { - GNUNET_break_op (0); - GNUNET_free (details); - GNUNET_JSON_parse_free (spec); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - pth->cb (pth->cb_cls, - &hr, - execution_time, - &total, - &wire_fee, - deposit_sums_length, - details); - GNUNET_free (details); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_transfers_post_cancel (pth); - return; - } - } - case MHD_HTTP_ACCEPTED: + case MHD_HTTP_NO_CONTENT: break; case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); /* Nothing really to verify, merchant says we need to authenticate. */ break; case MHD_HTTP_NOT_FOUND: @@ -202,44 +108,47 @@ handle_post_transfers_finished (void *cls, happen, we should pass the JSON reply to the application */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Did not find any data\n"); - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); break; case MHD_HTTP_BAD_GATEWAY: /* Exchange had an issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); { - uint32_t eec; uint32_t ehc; struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_uint32 ("exchange_code", - &eec), + TALER_JSON_spec_ec ("exchange_code", + &ptr.details.bad_gateway.exchange_ec), GNUNET_JSON_spec_uint32 ("exchange_http_status", &ehc), GNUNET_JSON_spec_end () }; if (GNUNET_OK != - GNUNET_JSON_parse (deposit_sum, + GNUNET_JSON_parse (ptr.hr.reply, ispec, NULL, NULL)) { GNUNET_break_op (0); + ptr.details.bad_gateway.exchange_http_status = 0; + ptr.details.bad_gateway.exchange_ec = TALER_EC_NONE; break; } else { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + ptr.details.bad_gateway.exchange_http_status + = (unsigned int) ehc; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Exchange returned %u/%u\n", - (unsigned int) eec, + (unsigned int) ptr.details.bad_gateway.exchange_ec, (unsigned int) ehc); } } @@ -247,28 +156,23 @@ handle_post_transfers_finished (void *cls, case MHD_HTTP_GATEWAY_TIMEOUT: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); break; default: /* unexpected response code */ GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, + TALER_MERCHANT_parse_error_details_ (ptr.hr.reply, response_code, - &hr); + &ptr.hr); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); + (unsigned int) ptr.hr.http_status, + (int) ptr.hr.ec); break; } pth->cb (pth->cb_cls, - &hr, - GNUNET_TIME_UNIT_FOREVER_TS, - NULL, - NULL, - 0, - NULL); + &ptr); TALER_MERCHANT_transfers_post_cancel (pth); } diff --git a/src/lib/merchant_api_post_using_templates.c b/src/lib/merchant_api_post_using_templates.c index d72768f3..f09c34cb 100644 --- a/src/lib/merchant_api_post_using_templates.c +++ b/src/lib/merchant_api_post_using_templates.c @@ -89,9 +89,9 @@ handle_post_using_templates_finished (void *cls, utph->job = NULL; TALER_MERCHANT_handle_order_creation_response_ (utph->cb, - utph->cb_cls, - response_code, - json); + utph->cb_cls, + response_code, + json); TALER_MERCHANT_using_templates_post_cancel (utph); } diff --git a/src/lib/merchant_api_tip_authorize.c b/src/lib/merchant_api_tip_authorize.c deleted file mode 100644 index 036d40af..00000000 --- a/src/lib/merchant_api_tip_authorize.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2017, 2020, 2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_tip_authorize.c - * @brief Implementation of the /tip-authorize request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - - -/** - * @brief A handle for tip authorizations. - */ -struct TALER_MERCHANT_TipAuthorizeHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipAuthorizeCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * We got a 200 response back from the exchange (or the merchant). - * Now we need to parse the response and if it is well-formed, - * call the callback (and set it to NULL afterwards). - * - * @param tao handle of the original authorization operation - * @param json cryptographic proof returned by the exchange/merchant - * @return #GNUNET_OK if response is valid - */ -static int -check_ok (struct TALER_MERCHANT_TipAuthorizeHandle *tao, - const json_t *json) -{ - const char *tip_status_url; - const char *taler_tip_uri; - struct TALER_TipIdentifierP tip_id; - struct GNUNET_TIME_Timestamp expiration_time; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("tip_status_url", &tip_status_url), - GNUNET_JSON_spec_string ("taler_tip_uri", &taler_tip_uri), - GNUNET_JSON_spec_timestamp ("tip_expiration", &expiration_time), - GNUNET_JSON_spec_fixed_auto ("tip_id", &tip_id), - GNUNET_JSON_spec_end () - }; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK, - .reply = json - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - char *log; - - GNUNET_break_op (0); - log = json_dumps (json, - 0); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "JSON %s\n", - log); - free (log); - return GNUNET_SYSERR; - } - tao->cb (tao->cb_cls, - &hr, - &tip_id, - taler_tip_uri, - expiration_time); - tao->cb = NULL; /* do not call twice */ - GNUNET_JSON_parse_free (spec); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /reservers/$TIP_ID/tip-authorize request. - * - * @param cls the `struct TALER_MERCHANT_TipAuthorizeHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_tip_authorize_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipAuthorizeHandle *tao = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tao->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - if (GNUNET_OK == - check_ok (tao, - json)) - { - TALER_MERCHANT_tip_authorize_cancel (tao); - return; - } - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* Well-defined status code, pass on to application! */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_PRECONDITION_FAILED: - /* Well-defined status code, pass on to application! */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_SERVICE_UNAVAILABLE: - /* Server had an unclear (internal or external) issue; we should retry, - but this API leaves this to the application */ - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - if (NULL != tao->cb) - tao->cb (tao->cb_cls, - &hr, - NULL, - NULL, - GNUNET_TIME_UNIT_ZERO_TS); - TALER_MERCHANT_tip_authorize_cancel (tao); -} - - -struct TALER_MERCHANT_TipAuthorizeHandle * -TALER_MERCHANT_tip_authorize2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_ReservePublicKeyP *reserve_pub, - const char *next_url, - const struct TALER_Amount *amount, - const char *justification, - TALER_MERCHANT_TipAuthorizeCallback authorize_cb, - void *authorize_cb_cls) -{ - struct TALER_MERCHANT_TipAuthorizeHandle *tao; - CURL *eh; - json_t *te_obj; - - tao = GNUNET_new (struct TALER_MERCHANT_TipAuthorizeHandle); - tao->ctx = ctx; - tao->cb = authorize_cb; - tao->cb_cls = authorize_cb_cls; - - { - char res_str[sizeof (*reserve_pub) * 2]; - char arg_str[sizeof (res_str) + 48]; - char *end; - - end = GNUNET_STRINGS_data_to_string (reserve_pub, - sizeof (*reserve_pub), - res_str, - sizeof (res_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "private/reserves/%s/authorize-tip", - res_str); - tao->url = TALER_url_join (backend_url, - arg_str, - NULL); - } - if (NULL == tao->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tao); - return NULL; - } - te_obj = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("amount", - amount), - GNUNET_JSON_pack_string ("justification", - justification), - GNUNET_JSON_pack_string ("next_url", - next_url)); - eh = curl_easy_init (); - GNUNET_assert (NULL != eh); - if (GNUNET_OK != - TALER_curl_easy_post (&tao->post_ctx, - eh, - te_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (te_obj); - GNUNET_free (tao->url); - GNUNET_free (tao); - return NULL; - } - - json_decref (te_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tao->url); - GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, - CURLOPT_URL, - tao->url)); - - tao->job = GNUNET_CURL_job_add2 (ctx, - eh, - tao->post_ctx.headers, - &handle_tip_authorize_finished, - tao); - return tao; -} - - -struct TALER_MERCHANT_TipAuthorizeHandle * -TALER_MERCHANT_tip_authorize (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *next_url, - const struct TALER_Amount *amount, - const char *justification, - TALER_MERCHANT_TipAuthorizeCallback authorize_cb, - void *authorize_cb_cls) -{ - struct TALER_MERCHANT_TipAuthorizeHandle *tao; - CURL *eh; - json_t *te_obj; - - tao = GNUNET_new (struct TALER_MERCHANT_TipAuthorizeHandle); - tao->ctx = ctx; - tao->cb = authorize_cb; - tao->cb_cls = authorize_cb_cls; - - tao->url = TALER_url_join (backend_url, - "private/tips", - NULL); - if (NULL == tao->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tao); - return NULL; - } - te_obj = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("amount", - amount), - GNUNET_JSON_pack_string ("justification", - justification), - GNUNET_JSON_pack_string ("next_url", - next_url)); - eh = TALER_MERCHANT_curl_easy_get_ (tao->url); - if (GNUNET_OK != - TALER_curl_easy_post (&tao->post_ctx, - eh, - te_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (te_obj); - GNUNET_free (tao->url); - GNUNET_free (tao); - return NULL; - } - json_decref (te_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tao->url); - tao->job = GNUNET_CURL_job_add2 (ctx, - eh, - tao->post_ctx.headers, - &handle_tip_authorize_finished, - tao); - return tao; -} - - -void -TALER_MERCHANT_tip_authorize_cancel ( - struct TALER_MERCHANT_TipAuthorizeHandle *tao) -{ - if (NULL != tao->job) - { - GNUNET_CURL_job_cancel (tao->job); - tao->job = NULL; - } - TALER_curl_easy_post_finished (&tao->post_ctx); - GNUNET_free (tao->url); - GNUNET_free (tao); -} - - -/* end of merchant_api_tip_authorize.c */ diff --git a/src/lib/merchant_api_tip_pickup.c b/src/lib/merchant_api_tip_pickup.c deleted file mode 100644 index 593efa43..00000000 --- a/src/lib/merchant_api_tip_pickup.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_tip_pickup.c - * @brief Implementation of the /tip-pickup request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - - -/** - * Data we keep per planchet. - */ -struct PlanchetData -{ - /** - * Secrets of the planchet. - */ - struct TALER_PlanchetMasterSecretP ps; - - /** - * Denomination key we are withdrawing. - */ - struct TALER_EXCHANGE_DenomPublicKey pk; - - /** - * Hash of the public key of the coin we are signing. - */ - struct TALER_CoinPubHashP c_hash; - - /** - * Nonce used for @e csr request, if any. - */ - struct TALER_CsNonce nonce; - - /** - * Handle for a /csr request we may optionally need - * to trigger. - */ - struct TALER_EXCHANGE_CsRWithdrawHandle *csr; - - /** - * Handle for the /tip-pickup operation we are part of. - */ - struct TALER_MERCHANT_TipPickupHandle *tp; - - /** - * Offset of this entry in the array. - */ - unsigned int off; -}; - - -/** - * Handle for a /tip-pickup operation. - */ -struct TALER_MERCHANT_TipPickupHandle -{ - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipPickupCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Handle for the actual (internal) withdraw operation. - */ - struct TALER_MERCHANT_TipPickup2Handle *tpo2; - - /** - * Array of length @e num_planchets. - */ - struct PlanchetData *planchets; - - /** - * Array of length @e num_planchets. - */ - struct TALER_EXCHANGE_PrivateCoinDetails *pcds; - - /** - * Context for making HTTP requests. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * URL of the merchant backend. - */ - char *backend_url; - - /** - * ID of the tip we are picking up. - */ - struct TALER_TipIdentifierP tip_id; - - /** - * Number of planchets/coins used for this operation. - */ - unsigned int num_planchets; - - /** - * Number of remaining active /csr-withdraw requests. - */ - unsigned int csr_active; -}; - - -/** - * Fail the pickup operation @a tp, returning @a ec. - * Also cancels @a tp. - * - * @param[in] tp operation to fail - * @param ec reason for the failure - */ -static void -fail_pickup (struct TALER_MERCHANT_TipPickupHandle *tp, - enum TALER_ErrorCode ec) -{ - struct TALER_MERCHANT_PickupDetails pd = { - .hr.ec = ec - }; - - tp->cb (tp->cb_cls, - &pd); - TALER_MERCHANT_tip_pickup_cancel (tp); -} - - -/** - * Callback for a /tip-pickup request. Returns the result of the operation. - * Note that the client MUST still do the unblinding of the @a blind_sigs. - * - * @param cls closure, a `struct TALER_MERCHANT_TipPickupHandle *` - * @param hr HTTP response details - * @param num_blind_sigs length of the @a reserve_sigs array, 0 on error - * @param blind_sigs array of blind signatures over the planchets, NULL on error - */ -static void -pickup_done_cb (void *cls, - const struct TALER_MERCHANT_HttpResponse *hr, - unsigned int num_blind_sigs, - const struct TALER_BlindedDenominationSignature *blind_sigs) -{ - struct TALER_MERCHANT_TipPickupHandle *tp = cls; - struct TALER_MERCHANT_PickupDetails pd = { - .hr = *hr - }; - - tp->tpo2 = NULL; - if (NULL == blind_sigs) - { - tp->cb (tp->cb_cls, - &pd); - TALER_MERCHANT_tip_pickup_cancel (tp); - return; - } - { - enum GNUNET_GenericReturnValue ok = GNUNET_OK; - - for (unsigned int i = 0; i<num_blind_sigs; i++) - { - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; - struct TALER_FreshCoin fc; - - if (GNUNET_OK != - TALER_planchet_to_coin (&tp->planchets[i].pk.key, - &blind_sigs[i], - &pcd->bks, - &pcd->coin_priv, - NULL, - &tp->planchets[i].c_hash, - &pcd->exchange_vals, - &fc)) - { - ok = GNUNET_SYSERR; - break; - } - pcd->sig = fc.sig; - } - if (GNUNET_OK != ok) - { - struct TALER_MERCHANT_HttpResponse hrx = { - .reply = hr->reply, - .ec = TALER_EC_MERCHANT_TIP_PICKUP_UNBLIND_FAILURE - }; - - pd.hr = hrx; - tp->cb (tp->cb_cls, - &pd); - } - else - { - pd.details.success.num_sigs = num_blind_sigs; - pd.details.success.pcds = tp->pcds; - tp->cb (tp->cb_cls, - &pd); - } - } - TALER_MERCHANT_tip_pickup_cancel (tp); -} - - -/** - * We have obtained all of the exchange inputs. Continue the pickup. - * - * @param[in,out] tp operation to continue - */ -static void -pickup_post_csr (struct TALER_MERCHANT_TipPickupHandle *tp) -{ - struct TALER_PlanchetDetail details[tp->num_planchets]; - - for (unsigned int i = 0; i<tp->num_planchets; i++) - { - const struct PlanchetData *pd = &tp->planchets[i]; - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; - - TALER_planchet_setup_coin_priv (&pd->ps, - &pcd->exchange_vals, - &pcd->coin_priv); - TALER_planchet_blinding_secret_create (&pd->ps, - &pcd->exchange_vals, - &pcd->bks); - if (TALER_DENOMINATION_CS == pcd->exchange_vals.cipher) - { - details[i].blinded_planchet.details.cs_blinded_planchet.nonce - = pd->nonce; - } - if (GNUNET_OK != - TALER_planchet_prepare (&pd->pk.key, - &pcd->exchange_vals, - &pcd->bks, - &pcd->coin_priv, - NULL, - &tp->planchets[i].c_hash, - &details[i])) - { - GNUNET_break (0); - for (unsigned int j = 0; j<i; j++) - TALER_planchet_detail_free (&details[j]); - fail_pickup (tp, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE); - return; - } - } - tp->tpo2 = TALER_MERCHANT_tip_pickup2 (tp->ctx, - tp->backend_url, - &tp->tip_id, - tp->num_planchets, - details, - &pickup_done_cb, - tp); - for (unsigned int j = 0; j<tp->num_planchets; j++) - TALER_planchet_detail_free (&details[j]); - if (NULL == tp->tpo2) - { - GNUNET_break (0); - fail_pickup (tp, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE); - return; - } -} - - -/** - * Callbacks of this type are used to serve the result of submitting a - * CS R request to a exchange. - * - * @param cls a `struct TALER_MERCHANT_TipPickupHandle` - * @param csrr response details - */ -static void -csr_cb (void *cls, - const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr) -{ - struct PlanchetData *pd = cls; - struct TALER_MERCHANT_TipPickupHandle *tp = pd->tp; - - pd->csr = NULL; - tp->csr_active--; - switch (csrr->hr.http_status) - { - case MHD_HTTP_OK: - { - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[pd->off]; - - pcd->exchange_vals = csrr->details.success.alg_values; - } - if (0 != tp->csr_active) - return; - pickup_post_csr (tp); - return; - default: - { - struct TALER_MERCHANT_PickupDetails pd = { - .hr.hint = "/csr-withdraw failed", - .hr.exchange_http_status = csrr->hr.http_status - }; - - tp->cb (tp->cb_cls, - &pd); - TALER_MERCHANT_tip_pickup_cancel (tp); - return; - } - } -} - - -struct TALER_MERCHANT_TipPickupHandle * -TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx, - struct TALER_EXCHANGE_Handle *exchange, - const char *backend_url, - const struct TALER_TipIdentifierP *tip_id, - unsigned int num_planchets, - const struct TALER_MERCHANT_PlanchetData *pds, - TALER_MERCHANT_TipPickupCallback pickup_cb, - void *pickup_cb_cls) -{ - struct TALER_MERCHANT_TipPickupHandle *tp; - - if (0 == num_planchets) - { - GNUNET_break (0); - return NULL; - } - tp = GNUNET_new (struct TALER_MERCHANT_TipPickupHandle); - tp->cb = pickup_cb; - tp->cb_cls = pickup_cb_cls; - tp->ctx = ctx; - tp->backend_url = GNUNET_strdup (backend_url); - tp->tip_id = *tip_id; - tp->num_planchets = num_planchets; - tp->planchets = GNUNET_new_array (num_planchets, - struct PlanchetData); - tp->pcds = GNUNET_new_array (num_planchets, - struct TALER_EXCHANGE_PrivateCoinDetails); - for (unsigned int i = 0; i<num_planchets; i++) - { - const struct TALER_MERCHANT_PlanchetData *mpd = &pds[i]; - const struct TALER_EXCHANGE_DenomPublicKey *pk = mpd->pk; - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; - struct PlanchetData *pd = &tp->planchets[i]; - - pd->off = i; - pd->tp = tp; - tp->planchets[i].ps = mpd->ps; - tp->planchets[i].pk = *pds[i].pk; - TALER_denom_pub_deep_copy (&tp->planchets[i].pk.key, - &pds[i].pk->key); - switch (pk->key.cipher) - { - case TALER_DENOMINATION_RSA: - pcd->exchange_vals.cipher = TALER_DENOMINATION_RSA; - break; - case TALER_DENOMINATION_CS: - { - TALER_cs_withdraw_nonce_derive (&pd->ps, - &pd->nonce); - pd->csr = TALER_EXCHANGE_csr_withdraw (exchange, - &pd->pk, - &pd->nonce, - &csr_cb, - pd); - if (NULL == pd->csr) - { - GNUNET_break (0); - TALER_MERCHANT_tip_pickup_cancel (tp); - return NULL; - } - tp->csr_active++; - break; - } - default: - GNUNET_break (0); - TALER_MERCHANT_tip_pickup_cancel (tp); - return NULL; - } - } - if (0 == tp->csr_active) - { - pickup_post_csr (tp); - return tp; - } - return tp; -} - - -void -TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupHandle *tp) -{ - for (unsigned int i = 0; i<tp->num_planchets; i++) - { - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; - struct PlanchetData *pd = &tp->planchets[i]; - - TALER_denom_sig_free (&pcd->sig); - TALER_denom_pub_free (&tp->planchets[i].pk.key); - if (NULL != pd->csr) - { - TALER_EXCHANGE_csr_withdraw_cancel (pd->csr); - pd->csr = NULL; - } - } - GNUNET_array_grow (tp->planchets, - tp->num_planchets, - 0); - if (NULL != tp->tpo2) - { - TALER_MERCHANT_tip_pickup2_cancel (tp->tpo2); - tp->tpo2 = NULL; - } - GNUNET_free (tp->backend_url); - GNUNET_free (tp->pcds); - GNUNET_free (tp); -} - - -/* end of merchant_api_tip_pickup.c */ diff --git a/src/lib/merchant_api_tip_pickup2.c b/src/lib/merchant_api_tip_pickup2.c deleted file mode 100644 index 33457024..00000000 --- a/src/lib/merchant_api_tip_pickup2.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_tip_pickup2.c - * @brief Implementation of the /tip-pickup request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - - -/** - * @brief A handle for tracking transactions. - */ -struct TALER_MERCHANT_TipPickup2Handle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipPickup2Callback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Expected number of planchets. - */ - unsigned int num_planchets; -}; - - -/** - * We got a 200 response back from the exchange (or the merchant). - * Now we need to parse the response and if it is well-formed, - * call the callback (and set it to NULL afterwards). - * - * @param tpo handle of the original authorization operation - * @param json cryptographic proof returned by the exchange/merchant - * @return #GNUNET_OK if response is valid - */ -static int -check_ok (struct TALER_MERCHANT_TipPickup2Handle *tpo, - const json_t *json) -{ - json_t *ja; - unsigned int ja_len; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("blind_sigs", &ja), - GNUNET_JSON_spec_end () - }; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK, - .reply = json - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - ja_len = json_array_size (ja); - if (ja_len != tpo->num_planchets) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - { - struct TALER_BlindedDenominationSignature mblind_sigs[ja_len]; - - for (unsigned int i = 0; i<ja_len; i++) - { - json_t *pj = json_array_get (ja, i); - struct GNUNET_JSON_Specification ispec[] = { - TALER_JSON_spec_blinded_denom_sig ("blind_sig", - &mblind_sigs[i]), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (pj, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - } - tpo->cb (tpo->cb_cls, - &hr, - ja_len, - mblind_sigs); - for (unsigned int i = 0; i<ja_len; i++) - TALER_blinded_denom_sig_free (&mblind_sigs[i]); - tpo->cb = NULL; /* do not call twice */ - } - GNUNET_JSON_parse_free (spec); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /track/transaction request. - * - * @param cls the `struct TALER_MERCHANT_TipPickupHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_tip_pickup_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipPickup2Handle *tpo = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tpo->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - if (GNUNET_OK != check_ok (tpo, - json)) - { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } - break; - case MHD_HTTP_BAD_REQUEST: - /* Can happen if we pickup an amount that exceeds the tip... */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_break (TALER_EC_MERCHANT_TIP_PICKUP_AMOUNT_EXCEEDS_TIP_REMAINING == - hr.ec); - break; - case MHD_HTTP_CONFLICT: - /* legal, can happen if we pickup a tip twice... */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - /* legal, can happen if tip ID is unknown */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - if (NULL != tpo->cb) - { - tpo->cb (tpo->cb_cls, - &hr, - 0, - NULL); - tpo->cb = NULL; - } - TALER_MERCHANT_tip_pickup2_cancel (tpo); -} - - -struct TALER_MERCHANT_TipPickup2Handle * -TALER_MERCHANT_tip_pickup2 (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_TipIdentifierP *tip_id, - unsigned int num_planchets, - const struct TALER_PlanchetDetail planchets[], - TALER_MERCHANT_TipPickup2Callback pickup_cb, - void *pickup_cb_cls) -{ - struct TALER_MERCHANT_TipPickup2Handle *tpo; - CURL *eh; - json_t *pa; - json_t *tp_obj; - - if (0 == num_planchets) - { - GNUNET_break (0); - return NULL; - } - pa = json_array (); - for (unsigned int i = 0; i<num_planchets; i++) - { - const struct TALER_PlanchetDetail *planchet = &planchets[i]; - json_t *p; - - p = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &planchet->denom_pub_hash), - TALER_JSON_pack_blinded_planchet ("coin_ev", - &planchet->blinded_planchet)); - if (0 != - json_array_append_new (pa, - p)) - { - GNUNET_break (0); - json_decref (pa); - return NULL; - } - } - tp_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("planchets", - pa)); - tpo = GNUNET_new (struct TALER_MERCHANT_TipPickup2Handle); - tpo->num_planchets = num_planchets; - tpo->ctx = ctx; - tpo->cb = pickup_cb; - tpo->cb_cls = pickup_cb_cls; - - { - char tip_str[sizeof (*tip_id) * 2]; - char arg_str[sizeof (tip_str) + 32]; - char *end; - - end = GNUNET_STRINGS_data_to_string (tip_id, - sizeof (*tip_id), - tip_str, - sizeof (tip_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "tips/%s/pickup", - tip_str); - tpo->url = TALER_url_join (backend_url, - arg_str, - NULL); - } - if (NULL == tpo->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (tp_obj); - GNUNET_free (tpo); - return NULL; - } - eh = TALER_MERCHANT_curl_easy_get_ (tpo->url); - if (GNUNET_OK != - TALER_curl_easy_post (&tpo->post_ctx, - eh, - tp_obj)) - { - GNUNET_break (0); - json_decref (tp_obj); - curl_easy_cleanup (eh); - GNUNET_free (tpo->url); - GNUNET_free (tpo); - return NULL; - } - json_decref (tp_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tpo->url); - tpo->job = GNUNET_CURL_job_add2 (ctx, - eh, - tpo->post_ctx.headers, - &handle_tip_pickup_finished, - tpo); - if (NULL == tpo->job) - { - TALER_MERCHANT_tip_pickup2_cancel (tpo); - return NULL; - } - return tpo; -} - - -void -TALER_MERCHANT_tip_pickup2_cancel ( - struct TALER_MERCHANT_TipPickup2Handle *tpo) -{ - if (NULL != tpo->job) - { - GNUNET_CURL_job_cancel (tpo->job); - tpo->job = NULL; - } - TALER_curl_easy_post_finished (&tpo->post_ctx); - GNUNET_free (tpo->url); - GNUNET_free (tpo); -} - - -/* end of merchant_api_tip_pickup2.c */ diff --git a/src/lib/merchant_api_wallet_get_order.c b/src/lib/merchant_api_wallet_get_order.c index 61bed534..763b2c83 100644 --- a/src/lib/merchant_api_wallet_get_order.c +++ b/src/lib/merchant_api_wallet_get_order.c @@ -116,11 +116,11 @@ handle_wallet_get_order_finished (void *cls, }; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_bool ("refunded", - &owgr.details.success.refunded), + &owgr.details.ok.refunded), GNUNET_JSON_spec_bool ("refund_pending", - &owgr.details.success.refund_pending), + &owgr.details.ok.refund_pending), TALER_JSON_spec_amount_any ("refund_amount", - &owgr.details.success.refund_amount), + &owgr.details.ok.refund_amount), GNUNET_JSON_spec_end () }; @@ -205,8 +205,7 @@ TALER_MERCHANT_wallet_order_get ( void *cb_cls) { struct TALER_MERCHANT_OrderWalletGetHandle *owgh; - unsigned long long tms; - long tlong; + unsigned int tms; GNUNET_assert (NULL != backend_url); GNUNET_assert (NULL != order_id); @@ -214,14 +213,8 @@ TALER_MERCHANT_wallet_order_get ( owgh->ctx = ctx; owgh->cb = cb; owgh->cb_cls = cb_cls; - tms = (unsigned long long) (timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); - /* set curl timeout to *our* long poll timeout plus one minute - (for network latency and processing delays) */ - tlong = (long) (GNUNET_TIME_relative_add (timeout, - GNUNET_TIME_UNIT_MINUTES). - rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); + tms = (unsigned int) (timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); { char timeout_ms[32]; struct GNUNET_CRYPTO_HashAsciiEncoded h_contract_s; @@ -231,7 +224,7 @@ TALER_MERCHANT_wallet_order_get ( &h_contract_s); GNUNET_snprintf (timeout_ms, sizeof (timeout_ms), - "%llu", + "%u", tms); GNUNET_asprintf (&path, "orders/%s", @@ -271,28 +264,10 @@ TALER_MERCHANT_wallet_order_get ( eh = TALER_MERCHANT_curl_easy_get_ (owgh->url); if (0 != tms) { - if (CURLE_OK != - curl_easy_setopt (eh, - CURLOPT_TIMEOUT_MS, - (long) tms)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - GNUNET_free (owgh->url); - GNUNET_free (owgh); - return NULL; - } - } - if (CURLE_OK != - curl_easy_setopt (eh, - CURLOPT_TIMEOUT_MS, - tlong)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - GNUNET_free (owgh->url); - GNUNET_free (owgh); - return NULL; + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 100L))); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, diff --git a/src/lib/merchant_api_wallet_get_template.c b/src/lib/merchant_api_wallet_get_template.c new file mode 100644 index 00000000..d9ca95bc --- /dev/null +++ b/src/lib/merchant_api_wallet_get_template.c @@ -0,0 +1,195 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_wallet_get_template.c + * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /templates/$ID operation. + */ +struct TALER_MERCHANT_WalletTemplateGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_WalletTemplateGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /templates/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TemplateGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_template_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_WalletTemplateGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /templates/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *contract; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_object_const ("template_contract", + &contract), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.details.ok.template_contract = contract; + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_wallet_template_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_wallet_template_get_cancel (tgh); +} + + +struct TALER_MERCHANT_WalletTemplateGetHandle * +TALER_MERCHANT_wallet_template_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + TALER_MERCHANT_WalletTemplateGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_WalletTemplateGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "templates/%s", + template_id); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_template_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_wallet_template_get_cancel ( + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_wallet_get_tip.c b/src/lib/merchant_api_wallet_get_tip.c deleted file mode 100644 index 49e507d0..00000000 --- a/src/lib/merchant_api_wallet_get_tip.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018, 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_wallet_get_tip.c - * @brief Implementation of the GET /tips/$TIP_ID request of the merchant's HTTP API - * @author Florian Dold - */ -#include "platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * @brief A handle for tracking /tip-get operations - */ -struct TALER_MERCHANT_TipWalletGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipWalletGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP /track/transaction request. - * - * @param cls the `struct TALER_MERCHANT_TipGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_wallet_tip_get_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipWalletGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /tip/$TIP_ID response with status code %u\n", - (unsigned int) response_code); - - tgh->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - { - const char *exchange_url; - struct TALER_Amount amount_remaining; - struct GNUNET_TIME_Timestamp expiration; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("expiration", - &expiration), - GNUNET_JSON_spec_string ("exchange_url", - &exchange_url), - TALER_JSON_spec_amount_any ("tip_amount", - &amount_remaining), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - tgh->cb (tgh->cb_cls, - &hr, - expiration, - exchange_url, - &amount_remaining); - TALER_MERCHANT_wallet_tip_get_cancel (tgh); - return; - } - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - /* legal, can happen if instance or tip reserve is unknown */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &hr, - GNUNET_TIME_UNIT_ZERO_TS, - NULL, - NULL); - TALER_MERCHANT_wallet_tip_get_cancel (tgh); -} - - -struct TALER_MERCHANT_TipWalletGetHandle * -TALER_MERCHANT_wallet_tip_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_TipIdentifierP *tip_id, - TALER_MERCHANT_TipWalletGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TipWalletGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_TipWalletGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - { - char res_str[sizeof (*tip_id) * 2]; - char arg_str[sizeof (res_str) + 48]; - char *end; - - end = GNUNET_STRINGS_data_to_string (tip_id, - sizeof (*tip_id), - res_str, - sizeof (res_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "tips/%s", - res_str); - tgh->url = TALER_url_join (backend_url, - arg_str, - NULL); - } - - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_wallet_tip_get_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_wallet_tip_get_cancel ( - struct TALER_MERCHANT_TipWalletGetHandle *tgh) -{ - if (NULL != tgh->job) - { - GNUNET_CURL_job_cancel (tgh->job); - tgh->job = NULL; - } - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} - - -/* end of merchant_api_wallet_get_tip.c */ diff --git a/src/lib/merchant_api_wallet_post_order_refund.c b/src/lib/merchant_api_wallet_post_order_refund.c index fc4b0abd..e72982f3 100644 --- a/src/lib/merchant_api_wallet_post_order_refund.c +++ b/src/lib/merchant_api_wallet_post_order_refund.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -32,6 +32,10 @@ #include <taler/taler_signatures.h> #include <taler/taler_curl_lib.h> +/** + * Maximum number of refunds we return. + */ +#define MAX_REFUNDS 1024 /** * Handle for a (public) POST /orders/ID/refund operation. @@ -71,33 +75,6 @@ struct TALER_MERCHANT_WalletOrderRefundHandle /** - * Convenience function to call the callback in @a owgh with an error code of - * @a ec and the exchange body being set to @a reply. - * - * @param orh handle providing callback - * @param ec error code to return to application - * @param reply JSON reply we got from the exchange, can be NULL - */ -static void -cb_failure (struct TALER_MERCHANT_WalletOrderRefundHandle *orh, - enum TALER_ErrorCode ec, - const json_t *reply) -{ - struct TALER_MERCHANT_HttpResponse hr = { - .ec = ec, - .reply = reply - }; - - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL, - NULL, - 0); -} - - -/** * Callback to process (public) POST /orders/ID/refund response * * @param cls the `struct TALER_MERCHANT_OrderRefundHandle` @@ -111,37 +88,31 @@ handle_refund_finished (void *cls, { struct TALER_MERCHANT_WalletOrderRefundHandle *orh = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json + struct TALER_MERCHANT_WalletRefundResponse wrr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; orh->job = NULL; - switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL, - NULL, - 0); + wrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: { - struct TALER_Amount refund_amount; - json_t *refunds; - struct TALER_MerchantPublicKeyP merchant_pub; + const json_t *refunds; unsigned int refund_len; struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ("refund_amount", - &refund_amount), - GNUNET_JSON_spec_json ("refunds", - &refunds), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), + TALER_JSON_spec_amount_any ( + "refund_amount", + &wrr.details.ok.refund_amount), + GNUNET_JSON_spec_array_const ( + "refunds", + &refunds), + GNUNET_JSON_spec_fixed_auto ( + "merchant_pub", + &wrr.details.ok.merchant_pub), GNUNET_JSON_spec_end () }; @@ -151,27 +122,21 @@ handle_refund_finished (void *cls, NULL, NULL)) { GNUNET_break_op (0); - cb_failure (orh, - TALER_EC_GENERIC_REPLY_MALFORMED, - json); - TALER_MERCHANT_wallet_post_order_refund_cancel (orh); - return; + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + break; } - - if (! json_is_array (refunds)) + refund_len = json_array_size (refunds); + if ( (json_array_size (refunds) != (size_t) refund_len) || + (refund_len > MAX_REFUNDS) ) { - GNUNET_break_op (0); - cb_failure (orh, - TALER_EC_GENERIC_REPLY_MALFORMED, - json); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_wallet_post_order_refund_cancel (orh); - return; + GNUNET_break (0); + wrr.hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; + wrr.hr.http_status = 0; + break; } - - refund_len = json_array_size (refunds); { - struct TALER_MERCHANT_RefundDetail rds[refund_len]; + struct TALER_MERCHANT_RefundDetail rds[GNUNET_NZL (refund_len)]; memset (rds, 0, @@ -183,10 +148,26 @@ handle_refund_finished (void *cls, i); const char *refund_status_type; uint32_t exchange_status; - int ret; + uint32_t eec = 0; struct GNUNET_JSON_Specification espec[] = { + GNUNET_JSON_spec_string ("type", + &refund_status_type), GNUNET_JSON_spec_uint32 ("exchange_status", &exchange_status), + GNUNET_JSON_spec_uint64 ("rtransaction_id", + &rd->rtransaction_id), + GNUNET_JSON_spec_fixed_auto ("coin_pub", + &rd->coin_pub), + TALER_JSON_spec_amount_any ("refund_amount", + &rd->refund_amount), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("exchange_reply", + &rd->hr.reply), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("exchange_code", + &eec), + NULL), GNUNET_JSON_spec_end () }; @@ -196,151 +177,85 @@ handle_refund_finished (void *cls, NULL, NULL)) { GNUNET_break_op (0); - cb_failure (orh, - TALER_EC_GENERIC_REPLY_MALFORMED, - json); - TALER_MERCHANT_wallet_post_order_refund_cancel (orh); - return; + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + goto finish; } - if (MHD_HTTP_OK == exchange_status) + rd->hr.http_status = exchange_status; + rd->hr.ec = (enum TALER_ErrorCode) eec; + switch (exchange_status) { - struct GNUNET_JSON_Specification rspec[] = { - GNUNET_JSON_spec_string ("type", - &refund_status_type), - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &rd->exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &rd->exchange_pub), - GNUNET_JSON_spec_uint64 ("rtransaction_id", - &rd->rtransaction_id), - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &rd->coin_pub), - TALER_JSON_spec_amount_any ("refund_amount", - &rd->refund_amount), - GNUNET_JSON_spec_end () - }; - - ret = GNUNET_JSON_parse (jrefund, - rspec, - NULL, NULL); - if (GNUNET_OK == ret) + case MHD_HTTP_OK: { - /* check that type field is correct */ - if (0 != strcmp ("success", refund_status_type)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - } - } - } - else - { - struct GNUNET_JSON_Specification rspec[] = { - GNUNET_JSON_spec_string ("type", - &refund_status_type), - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &rd->coin_pub), - GNUNET_JSON_spec_uint64 ("rtransaction_id", - &rd->rtransaction_id), - TALER_JSON_spec_amount_any ("refund_amount", - &rd->refund_amount), - GNUNET_JSON_spec_end () - }; - - ret = GNUNET_JSON_parse (jrefund, + struct GNUNET_JSON_Specification rspec[] = { + GNUNET_JSON_spec_fixed_auto ("exchange_sig", + &rd->details.ok.exchange_sig), + GNUNET_JSON_spec_fixed_auto ("exchange_pub", + &rd->details.ok.exchange_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (jrefund, rspec, - NULL, NULL); - if (GNUNET_OK == ret) - { - /* parse optional arguments */ - json_t *jec; - - jec = json_object_get (jrefund, - "exchange_code"); - if (NULL != jec) + NULL, + NULL)) { - if (! json_is_integer (jec)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - } - else - { - rd->hr.ec = (enum TALER_ErrorCode) json_integer_value (jec); - } + GNUNET_break_op (0); + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + goto finish; } - rd->hr.reply = json_object_get (jrefund, - "exchange_reply"); /* check that type field is correct */ - if (0 != strcmp ("failure", refund_status_type)) + if (0 != strcmp ("success", + refund_status_type)) { GNUNET_break_op (0); - ret = GNUNET_SYSERR; + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + goto finish; } } - } - if (GNUNET_OK != ret) - { - GNUNET_break_op (0); - cb_failure (orh, - TALER_EC_GENERIC_REPLY_MALFORMED, - json); - TALER_MERCHANT_wallet_post_order_refund_cancel (orh); - return; - } - rd->hr.http_status = exchange_status; - } - - { - struct TALER_MERCHANT_HttpResponse hr = { - .reply = json, - .http_status = MHD_HTTP_OK - }; + break; /* end MHD_HTTP_OK */ + default: + if (0 != strcmp ("failure", + refund_status_type)) + { + GNUNET_break_op (0); + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + goto finish; + } + } /* switch on exchange status code */ + } /* for all refunds */ - orh->cb (orh->cb_cls, - &hr, - &refund_amount, - &merchant_pub, - rds, - refund_len); - } - } - GNUNET_JSON_parse_free (spec); - } + wrr.details.ok.refunds = rds; + wrr.details.ok.refunds_length = refund_len; + orh->cb (orh->cb_cls, + &wrr); + TALER_MERCHANT_wallet_post_order_refund_cancel (orh); + return; + } /* end 'rds' scope */ + } /* case MHD_HTTP_OK */ break; case MHD_HTTP_NO_CONTENT: - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL, - NULL, - 0); break; case MHD_HTTP_CONFLICT: case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL, - NULL, - 0); + wrr.hr.ec = TALER_JSON_get_error_code (json); + wrr.hr.hint = TALER_JSON_get_error_hint (json); break; default: GNUNET_break_op (0); /* unexpected status code */ TALER_MERCHANT_parse_error_details_ (json, response_code, - &hr); - orh->cb (orh->cb_cls, - &hr, - NULL, - NULL, - NULL, - 0); + &wrr.hr); break; } +finish: + orh->cb (orh->cb_cls, + &wrr); TALER_MERCHANT_wallet_post_order_refund_cancel (orh); } |