diff options
author | Christian Blättler <blatc2@bfh.ch> | 2024-06-13 11:34:46 +0200 |
---|---|---|
committer | Christian Blättler <blatc2@bfh.ch> | 2024-06-13 11:34:46 +0200 |
commit | 599db2c13cc7b4eddab22f1e8ef55dde23086f68 (patch) | |
tree | 53f1060f6507cffdaf3a139428d7ab4614b13efa /src/backend | |
parent | b9315cf4675bfb2ae94b71cf8931963b27873a5b (diff) | |
parent | 2e8cfbe844d4fbcf5fa6f086be0b184821e1bdc2 (diff) |
Merge branch 'master' into tokens-payment
# Conflicts:
# src/backend/taler-merchant-httpd_private-post-orders.c
# src/backenddb/Makefile.am
# src/backenddb/merchant-0006.sql
# src/backenddb/pg_insert_token_family_key.c
Diffstat (limited to 'src/backend')
32 files changed, 1526 insertions, 429 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index dbc7cde8..cce7faec 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -37,6 +37,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_mhd.h \ taler-merchant-httpd_private-delete-account-ID.c \ taler-merchant-httpd_private-delete-account-ID.h \ + taler-merchant-httpd_private-delete-categories-ID.c \ + taler-merchant-httpd_private-delete-categories-ID.h \ taler-merchant-httpd_private-delete-instances-ID.c \ taler-merchant-httpd_private-delete-instances-ID.h \ taler-merchant-httpd_private-delete-instances-ID-token.c \ @@ -59,12 +61,18 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-get-accounts.h \ taler-merchant-httpd_private-get-accounts-ID.c \ taler-merchant-httpd_private-get-accounts-ID.h \ + taler-merchant-httpd_private-get-categories.c \ + taler-merchant-httpd_private-get-categories.h \ + taler-merchant-httpd_private-get-categories-ID.c \ + taler-merchant-httpd_private-get-categories-ID.h \ taler-merchant-httpd_private-get-instances.c \ taler-merchant-httpd_private-get-instances.h \ taler-merchant-httpd_private-get-instances-ID.c \ taler-merchant-httpd_private-get-instances-ID.h \ taler-merchant-httpd_private-get-instances-ID-kyc.c \ taler-merchant-httpd_private-get-instances-ID-kyc.h \ + taler-merchant-httpd_private-get-pos.c \ + taler-merchant-httpd_private-get-pos.h \ taler-merchant-httpd_private-get-products.c \ taler-merchant-httpd_private-get-products.h \ taler-merchant-httpd_private-get-products-ID.c \ @@ -93,6 +101,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-get-webhooks-ID.h \ taler-merchant-httpd_private-patch-accounts-ID.c \ taler-merchant-httpd_private-patch-accounts-ID.h \ + taler-merchant-httpd_private-patch-categories-ID.c \ + taler-merchant-httpd_private-patch-categories-ID.h \ taler-merchant-httpd_private-patch-instances-ID.c \ taler-merchant-httpd_private-patch-instances-ID.h \ taler-merchant-httpd_private-patch-orders-ID-forget.c \ @@ -109,6 +119,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-patch-webhooks-ID.h \ taler-merchant-httpd_private-post-account.c \ taler-merchant-httpd_private-post-account.h \ + taler-merchant-httpd_private-post-categories.c \ + taler-merchant-httpd_private-post-categories.h \ taler-merchant-httpd_private-post-instances.c \ taler-merchant-httpd_private-post-instances.h \ taler-merchant-httpd_private-post-instances-ID-auth.c \ diff --git a/src/backend/taler-merchant-exchange.c b/src/backend/taler-merchant-exchange.c index 7945cb50..72566e9b 100644 --- a/src/backend/taler-merchant-exchange.c +++ b/src/backend/taler-merchant-exchange.c @@ -32,8 +32,8 @@ * long-polling and do not want to wake up too often. */ #define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, \ - 30) + GNUNET_TIME_UNIT_MINUTES, \ + 30) /** * How many inquiries do we process concurrently at most. @@ -1154,7 +1154,8 @@ find_work (void *cls) return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No open inquiries found, waiting for notification to resume\n"); + "No open inquiries found, waiting for notification to resume\n") + ; } } @@ -1215,7 +1216,7 @@ run (void *cls, { GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); - global_ret = EXIT_NO_RESTART; + global_ret = EXIT_FAILURE; return; } if (NULL == @@ -1233,7 +1234,7 @@ run (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to connect to database\n"); GNUNET_SCHEDULER_shutdown (); - global_ret = EXIT_NO_RESTART; + global_ret = EXIT_FAILURE; return; } { diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 95ba9bf1..a7c1cbb4 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -34,6 +34,7 @@ #include "taler-merchant-httpd_get-templates-ID.h" #include "taler-merchant-httpd_mhd.h" #include "taler-merchant-httpd_private-delete-account-ID.h" +#include "taler-merchant-httpd_private-delete-categories-ID.h" #include "taler-merchant-httpd_private-delete-instances-ID.h" #include "taler-merchant-httpd_private-delete-instances-ID-token.h" #include "taler-merchant-httpd_private-delete-products-ID.h" @@ -45,9 +46,12 @@ #include "taler-merchant-httpd_private-delete-webhooks-ID.h" #include "taler-merchant-httpd_private-get-accounts.h" #include "taler-merchant-httpd_private-get-accounts-ID.h" +#include "taler-merchant-httpd_private-get-categories.h" +#include "taler-merchant-httpd_private-get-categories-ID.h" #include "taler-merchant-httpd_private-get-instances.h" #include "taler-merchant-httpd_private-get-instances-ID.h" #include "taler-merchant-httpd_private-get-instances-ID-kyc.h" +#include "taler-merchant-httpd_private-get-pos.h" #include "taler-merchant-httpd_private-get-products.h" #include "taler-merchant-httpd_private-get-products-ID.h" #include "taler-merchant-httpd_private-get-orders.h" @@ -62,6 +66,7 @@ #include "taler-merchant-httpd_private-get-webhooks.h" #include "taler-merchant-httpd_private-get-webhooks-ID.h" #include "taler-merchant-httpd_private-patch-accounts-ID.h" +#include "taler-merchant-httpd_private-patch-categories-ID.h" #include "taler-merchant-httpd_private-patch-instances-ID.h" #include "taler-merchant-httpd_private-patch-orders-ID-forget.h" #include "taler-merchant-httpd_private-patch-otp-devices-ID.h" @@ -70,6 +75,7 @@ #include "taler-merchant-httpd_private-patch-token-families-SLUG.h" #include "taler-merchant-httpd_private-patch-webhooks-ID.h" #include "taler-merchant-httpd_private-post-account.h" +#include "taler-merchant-httpd_private-post-categories.h" #include "taler-merchant-httpd_private-post-instances.h" #include "taler-merchant-httpd_private-post-instances-ID-auth.h" #include "taler-merchant-httpd_private-post-instances-ID-token.h" @@ -932,6 +938,52 @@ url_handler (void *cls, .method = MHD_HTTP_METHOD_GET, .handler = &TMH_private_get_instances_ID_kyc, }, + /* GET /pos: */ + { + .url_prefix = "/pos", + .method = MHD_HTTP_METHOD_GET, + .handler = &TMH_private_get_pos + }, + /* GET /categories: */ + { + .url_prefix = "/categories", + .method = MHD_HTTP_METHOD_GET, + .handler = &TMH_private_get_categories + }, + /* POST /categories: */ + { + .url_prefix = "/categories", + .method = MHD_HTTP_METHOD_POST, + .handler = &TMH_private_post_categories, + /* allow category data of up to 8 kb, that should be plenty */ + .max_upload = 1024 * 8 + }, + /* GET /categories/$ID: */ + { + .url_prefix = "/categories/", + .method = MHD_HTTP_METHOD_GET, + .have_id_segment = true, + .allow_deleted_instance = true, + .handler = &TMH_private_get_categories_ID + }, + /* DELETE /categories/$ID: */ + { + .url_prefix = "/categories/", + .method = MHD_HTTP_METHOD_DELETE, + .have_id_segment = true, + .allow_deleted_instance = true, + .handler = &TMH_private_delete_categories_ID + }, + /* PATCH /categories/$ID/: */ + { + .url_prefix = "/categories/", + .method = MHD_HTTP_METHOD_PATCH, + .have_id_segment = true, + .allow_deleted_instance = true, + .handler = &TMH_private_patch_categories_ID, + /* allow category data of up to 8 kb, that should be plenty */ + .max_upload = 1024 * 8 + }, /* GET /products: */ { .url_prefix = "/products", @@ -949,7 +1001,7 @@ url_handler (void *cls, in the code... */ .max_upload = 1024 * 1024 * 8 }, - /* GET /products/$ID/: */ + /* GET /products/$ID: */ { .url_prefix = "/products/", .method = MHD_HTTP_METHOD_GET, diff --git a/src/backend/taler-merchant-httpd_config.c b/src/backend/taler-merchant-httpd_config.c index 3777904f..10f0cd39 100644 --- a/src/backend/taler-merchant-httpd_config.c +++ b/src/backend/taler-merchant-httpd_config.c @@ -42,7 +42,7 @@ * #MERCHANT_PROTOCOL_CURRENT and #MERCHANT_PROTOCOL_AGE in * merchant_api_config.c! */ -#define MERCHANT_PROTOCOL_VERSION "14:0:10" +#define MERCHANT_PROTOCOL_VERSION "16:0:12" /** @@ -82,14 +82,20 @@ MH_handler_config (struct TMH_RequestHandler *rh, struct TMH_HandlerContext *hc) { static struct MHD_Response *response; + static struct GNUNET_TIME_Absolute a; (void) rh; (void) hc; + if ( (GNUNET_TIME_absolute_is_past (a)) && + (NULL != response) ) + { + MHD_destroy_response (response); + response = NULL; + } if (NULL == response) { json_t *specs = json_object (); json_t *exchanges = json_array (); - struct GNUNET_TIME_Absolute a; struct GNUNET_TIME_Timestamp km; char dat[128]; diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h index 006d0205..6389cc70 100644 --- a/src/backend/taler-merchant-httpd_contract.h +++ b/src/backend/taler-merchant-httpd_contract.h @@ -367,7 +367,7 @@ struct TALER_MerchantContractTokenFamily }; /** - * Struct to hold contract terms in v0 and v1 format. v0 contracts are mdoelled + * Struct to hold contract terms in v0 and v1 format. v0 contracts are modelled * as a v1 contract with a single choice and no inputs and outputs. Use the * version field to explicitly differentiate between v0 and v1 contracts. */ diff --git a/src/backend/taler-merchant-httpd_mhd.c b/src/backend/taler-merchant-httpd_mhd.c index e96acca5..0bb22b35 100644 --- a/src/backend/taler-merchant-httpd_mhd.c +++ b/src/backend/taler-merchant-httpd_mhd.c @@ -59,6 +59,7 @@ TMH_MHD_test_html_desired (struct MHD_Connection *connection) bool ret = false; const char *accept; + // FIXME: use TALER_MHD_check_accept here! accept = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT); diff --git a/src/backend/taler-merchant-httpd_private-delete-categories-ID.c b/src/backend/taler-merchant-httpd_private-delete-categories-ID.c new file mode 100644 index 00000000..892dbd9c --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-delete-categories-ID.c @@ -0,0 +1,92 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-delete-categories-ID.c + * @brief implement DELETE /private/categories/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-delete-categories-ID.h" +#include <taler/taler_json_lib.h> + + +/** + * Handle a DELETE "/categories/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_delete_categories_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + enum GNUNET_DB_QueryStatus qs; + unsigned long long cnum; + char dummy; + + (void) rh; + GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != hc->infix); + if (1 != sscanf (hc->infix, + "%llu%c", + &cnum, + &dummy)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "category_id must be a number"); + } + qs = TMH_db->delete_category (TMH_db->cls, + mi->settings.id, + cnum); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "delete_category"); + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "delete_category"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_CATEGORY_UNKNOWN, + hc->infix); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + } + GNUNET_assert (0); + return MHD_NO; +} + + +/* end of taler-merchant-httpd_private-delete-categories-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-delete-categories-ID.h b/src/backend/taler-merchant-httpd_private-delete-categories-ID.h new file mode 100644 index 00000000..b17eed49 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-delete-categories-ID.h @@ -0,0 +1,42 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-delete-categories-ID.h + * @brief implement DELETE /private/categories/$ID/ + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_DELETE_CATEGORIES_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_DELETE_CATEGORIES_ID_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a DELETE "/categories/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_delete_categories_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-delete-categories-ID.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c index b147b84f..ff9ad2ef 100644 --- a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c +++ b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c @@ -61,7 +61,7 @@ TMH_private_delete_otp_devices_ID (const struct TMH_RequestHandler *rh, case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN, + TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, hc->infix); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: return TALER_MHD_reply_static (connection, diff --git a/src/backend/taler-merchant-httpd_private-delete-products-ID.c b/src/backend/taler-merchant-httpd_private-delete-products-ID.c index 7d314785..23b6de3c 100644 --- a/src/backend/taler-merchant-httpd_private-delete-products-ID.c +++ b/src/backend/taler-merchant-httpd_private-delete-products-ID.c @@ -59,22 +59,30 @@ TMH_private_delete_products_ID (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, "delete_product (soft)"); case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* check if deletion must have failed because of locks by - checking if the product exists */ - qs = TMH_db->lookup_product (TMH_db->cls, - mi->settings.id, - hc->infix, - NULL); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "lookup_product"); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN, - hc->infix); + { + size_t num_categories = 0; + uint64_t *categories = NULL; + + /* check if deletion must have failed because of locks by + checking if the product exists */ + qs = TMH_db->lookup_product (TMH_db->cls, + mi->settings.id, + hc->infix, + NULL, + &num_categories, + &categories); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "lookup_product"); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN, + hc->infix); + GNUNET_free (categories); + } return TALER_MHD_reply_with_error ( connection, MHD_HTTP_CONFLICT, diff --git a/src/backend/taler-merchant-httpd_private-get-categories-ID.c b/src/backend/taler-merchant-httpd_private-get-categories-ID.c new file mode 100644 index 00000000..02ef3495 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-categories-ID.c @@ -0,0 +1,119 @@ +/* + This file is part of TALER + (C) 2022-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-categories-ID.c + * @brief implement GET /private/categories/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-get-categories-ID.h" +#include <taler/taler_json_lib.h> + + +/** + * Handle a GET "/private/categories/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_categories_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + enum GNUNET_DB_QueryStatus qs; + unsigned long long cnum; + char dummy; + struct TALER_MERCHANTDB_CategoryDetails cd; + + GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != hc->infix); + if (1 != sscanf (hc->infix, + "%llu%c", + &cnum, + &dummy)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "category_id must be a number"); + } + + qs = TMH_db->select_category (TMH_db->cls, + mi->settings.id, + cnum, + &cd); + if (0 > qs) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select_category"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_CATEGORY_UNKNOWN, + hc->infix); + } + { + MHD_RESULT ret; + json_t *products; + + products = json_array (); + GNUNET_assert (NULL != products); + for (unsigned int i = 0; i<cd.num_products; i++) + { + const struct TALER_MERCHANTDB_ProductSummary *product + = &cd.products[i]; + json_t *jprod; + + jprod = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("product_id", + product->product_id), + GNUNET_JSON_pack_string ("description", + product->description), + GNUNET_JSON_pack_object_steal ("description_i18n", + product->description_i18n)); + GNUNET_assert (0 == + json_array_append_new (products, + jprod)); + } + + ret = TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_string ("name", + cd.category_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("name_i18n", + cd.category_name_i18n)), + GNUNET_JSON_pack_array_steal ("products", + products)); + TALER_MERCHANTDB_category_details_free (&cd); + return ret; + } +} + + +/* end of taler-merchant-httpd_private-get-categories-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-get-categories-ID.h b/src/backend/taler-merchant-httpd_private-get-categories-ID.h new file mode 100644 index 00000000..c0226659 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-categories-ID.h @@ -0,0 +1,42 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-categories-ID.h + * @brief implement GET /private/categories/$ID/ + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_CATEGORIES_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_GET_CATEGORIES_ID_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a GET "/private/categories/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_categories_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-get-categories-ID.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-categories.c b/src/backend/taler-merchant-httpd_private-get-categories.c new file mode 100644 index 00000000..8ebccb2b --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-categories.c @@ -0,0 +1,93 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-categories.c + * @brief implement GET /categories + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-get-categories.h" + + +/** + * Add category details to our JSON array. + * + * @param cls a `json_t *` JSON array to build + * @param category_id ID of the category + * @param category_name name of the category + * @param category_name_i18n translations of the @a category_name + * @param product_count number of products in the category + */ +static void +add_category (void *cls, + uint64_t category_id, + const char *category_name, + const json_t *category_name_i18n, + uint64_t product_count) +{ + json_t *pa = cls; + + GNUNET_assert ( + 0 == + json_array_append_new ( + pa, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_uint64 ( + "category_id", + category_id), + GNUNET_JSON_pack_string ( + "name", + category_name), + GNUNET_JSON_pack_object_incref ( + "name_i18n", + (json_t *) category_name_i18n), + GNUNET_JSON_pack_uint64 ( + "product_count", + product_count)))); +} + + +MHD_RESULT +TMH_private_get_categories (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + json_t *pa; + enum GNUNET_DB_QueryStatus qs; + + pa = json_array (); + GNUNET_assert (NULL != pa); + qs = TMH_db->lookup_categories (TMH_db->cls, + hc->instance->settings.id, + &add_category, + pa); + if (0 > qs) + { + GNUNET_break (0); + json_decref (pa); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + } + return TALER_MHD_REPLY_JSON_PACK (connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_array_steal ("categories", + pa)); +} + + +/* end of taler-merchant-httpd_private-get-categories.c */ diff --git a/src/backend/taler-merchant-httpd_private-get-categories.h b/src/backend/taler-merchant-httpd_private-get-categories.h new file mode 100644 index 00000000..68eed05e --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-categories.h @@ -0,0 +1,42 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-categories.h + * @brief implement GET /private/categories + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_CATEGORIES_H +#define TALER_MERCHANT_HTTPD_PRIVATE_GET_CATEGORIES_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a GET "/private/categories" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_categories ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-get-categories.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c index 8a338e7d..0382b742 100644 --- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c +++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c @@ -684,7 +684,7 @@ kyc_with_exchange (void *cls, keys, ekr->exchange_kyc_serial, &h_payto, - ekr->kc->mi->settings.ut, + 1, /* FIXME: this will go away! */ GNUNET_TIME_absolute_get_remaining (kc->timeout), &exchange_check_cb, ekr); diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID.c b/src/backend/taler-merchant-httpd_private-get-instances-ID.c index adc99c39..c8cd2c12 100644 --- a/src/backend/taler-merchant-httpd_private-get-instances-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-instances-ID.c @@ -79,7 +79,7 @@ get_instances_ID (struct TMH_MerchantInstance *mi, mi->settings.name), GNUNET_JSON_pack_string ( "user_type", - TALER_KYCLOGIC_kyc_user_type2s (mi->settings.ut)), + "business"), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("website", mi->settings.website)), diff --git a/src/backend/taler-merchant-httpd_private-get-instances.c b/src/backend/taler-merchant-httpd_private-get-instances.c index 50b5c0c2..0220d4df 100644 --- a/src/backend/taler-merchant-httpd_private-get-instances.c +++ b/src/backend/taler-merchant-httpd_private-get-instances.c @@ -76,7 +76,7 @@ add_instance (void *cls, mi->settings.name), GNUNET_JSON_pack_string ( "user_type", - TALER_KYCLOGIC_kyc_user_type2s (mi->settings.ut)), + "business"), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("website", mi->settings.website)), diff --git a/src/backend/taler-merchant-httpd_private-get-pos.c b/src/backend/taler-merchant-httpd_private-get-pos.c new file mode 100644 index 00000000..4c14b6ae --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-pos.c @@ -0,0 +1,148 @@ +/* + This file is part of TALER + (C) 2019, 2020, 2021, 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-pos.c + * @brief implement GET /private/pos + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-get-pos.h" + + +/** + * Closure for add_product(). + */ +struct Context +{ + /** + * JSON array of products we are building. + */ + json_t *pa; + + /** + * JSON array of categories we are building. + */ + json_t *ca; + +}; + + +/** + * Add product details to our JSON array. + * + * @param ctx a `struct Context` with JSON arrays to build + * @param product_id ID of the product + */ +static void +add_product (void *cls, + uint64_t product_serial, + const char *product_id, + const struct TALER_MERCHANTDB_ProductDetails *pd, + size_t num_categories, + const uint64_t *categories) +{ + struct Context *ctx = cls; + json_t *pa = ctx->pa; + json_t *cata; + + cata = json_array (); + GNUNET_assert (NULL != cata); + for (size_t i = 0; i<num_categories; i++) + GNUNET_assert ( + 0 == json_array_append_new ( + cata, + json_integer (categories[i]))); + GNUNET_assert ( + 0 == + json_array_append_new ( + pa, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("description", + pd->description), + GNUNET_JSON_pack_object_incref ("description_i18n", + (json_t *) pd->description_i18n), + GNUNET_JSON_pack_string ("unit", + pd->unit), + TALER_JSON_pack_amount ("price", + &pd->price), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("image", + pd->image)), + GNUNET_JSON_pack_array_steal ("categories", + cata), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_incref ("taxes", + (json_t *) pd->taxes)), + (INT64_MAX == pd->total_stock) + ? GNUNET_JSON_pack_int64 ("total_stock", + pd->total_stock) + : GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("total_stock", + NULL)), + GNUNET_JSON_pack_uint64 ("minimum_age", + pd->minimum_age), + GNUNET_JSON_pack_uint64 ("product_serial", + product_serial), + GNUNET_JSON_pack_string ("product_id", + product_id)))); +} + + +MHD_RESULT +TMH_private_get_pos (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct Context ctx; + enum GNUNET_DB_QueryStatus qs; + + ctx.pa = json_array (); + GNUNET_assert (NULL != ctx.pa); + ctx.ca = json_array (); + GNUNET_assert (NULL != ctx.ca); + GNUNET_assert ( + 0 == json_array_append_new ( + ctx.ca, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_uint64 ("id", + 0), + GNUNET_JSON_pack_string ("name", + "default")))); + qs = TMH_db->lookup_all_products (TMH_db->cls, + hc->instance->settings.id, + &add_product, + &ctx); + if (0 > qs) + { + GNUNET_break (0); + json_decref (ctx.pa); + json_decref (ctx.ca); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + } + return TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_array_steal ("categories", + ctx.ca), + GNUNET_JSON_pack_array_steal ("products", + ctx.pa)); +} + + +/* end of taler-merchant-httpd_private-get-pos.c */ diff --git a/src/backend/taler-merchant-httpd_private-get-pos.h b/src/backend/taler-merchant-httpd_private-get-pos.h new file mode 100644 index 00000000..ce266823 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-pos.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-pos.h + * @brief implement GET /pos + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_POS_H +#define TALER_MERCHANT_HTTPD_PRIVATE_GET_POS_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a GET "/pos" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_pos (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-get-pos.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-products-ID.c b/src/backend/taler-merchant-httpd_private-get-products-ID.c index 1406349f..0729b1df 100644 --- a/src/backend/taler-merchant-httpd_private-get-products-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-products-ID.c @@ -32,19 +32,25 @@ * @return MHD result code */ MHD_RESULT -TMH_private_get_products_ID (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) +TMH_private_get_products_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { struct TMH_MerchantInstance *mi = hc->instance; struct TALER_MERCHANTDB_ProductDetails pd = { 0 }; enum GNUNET_DB_QueryStatus qs; + size_t num_categories = 0; + uint64_t *categories = NULL; + json_t *jcategories; GNUNET_assert (NULL != mi); qs = TMH_db->lookup_product (TMH_db->cls, mi->settings.id, hc->infix, - &pd); + &pd, + &num_categories, + &categories); if (0 > qs) { GNUNET_break (0); @@ -60,6 +66,16 @@ TMH_private_get_products_ID (const struct TMH_RequestHandler *rh, TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN, hc->infix); } + jcategories = json_array (); + GNUNET_assert (NULL != jcategories); + for (size_t i = 0; i<num_categories; i++) + { + GNUNET_assert (0 == + json_array_append_new (jcategories, + json_integer (categories[i]))); + } + GNUNET_free (categories); + { MHD_RESULT ret; @@ -72,12 +88,16 @@ TMH_private_get_products_ID (const struct TMH_RequestHandler *rh, pd.description_i18n), GNUNET_JSON_pack_string ("unit", pd.unit), + GNUNET_JSON_pack_array_steal ("categories", + jcategories), TALER_JSON_pack_amount ("price", &pd.price), - GNUNET_JSON_pack_string ("image", - pd.image), - GNUNET_JSON_pack_array_steal ("taxes", - pd.taxes), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("image", + pd.image)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("taxes", + pd.taxes)), GNUNET_JSON_pack_int64 ("total_stock", (INT64_MAX == pd.total_stock) ? -1LL @@ -86,8 +106,9 @@ TMH_private_get_products_ID (const struct TMH_RequestHandler *rh, pd.total_sold), GNUNET_JSON_pack_uint64 ("total_lost", pd.total_lost), - GNUNET_JSON_pack_object_steal ("address", - pd.address), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_steal ("address", + pd.address)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_timestamp ("next_restock", (pd.next_restock))), diff --git a/src/backend/taler-merchant-httpd_private-patch-categories-ID.c b/src/backend/taler-merchant-httpd_private-patch-categories-ID.c new file mode 100644 index 00000000..1aa489cf --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-patch-categories-ID.c @@ -0,0 +1,120 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + 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 General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-patch-categories-ID.c + * @brief implementing PATCH /categories/$ID request handling + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-patch-categories-ID.h" +#include "taler-merchant-httpd_helper.h" +#include <taler/taler_json_lib.h> + + +MHD_RESULT +TMH_private_patch_categories_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + unsigned long long cnum; + char dummy; + const char *category_name; + const json_t *category_name_i18n; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("name", + &category_name), + GNUNET_JSON_spec_object_const ("name_i18n", + &category_name_i18n), + GNUNET_JSON_spec_end () + }; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != hc->infix); + if (1 != sscanf (hc->infix, + "%llu%c", + &cnum, + &dummy)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "category_id must be a number"); + } + + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + + qs = TMH_db->update_category (TMH_db->cls, + mi->settings.id, + cnum, + category_name, + category_name_i18n); + { + MHD_RESULT ret = MHD_NO; + + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "update_category"); + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "unexpected serialization problem"); + break; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_CATEGORY_UNKNOWN, + category_name); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + ret = TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + break; + } + GNUNET_JSON_parse_free (spec); + return ret; + } +} + + +/* end of taler-merchant-httpd_private-patch-categories-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-patch-categories-ID.h b/src/backend/taler-merchant-httpd_private-patch-categories-ID.h new file mode 100644 index 00000000..c88290a8 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-patch-categories-ID.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + 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 General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-patch-categories-ID.h + * @brief implementing PATCH /private/categories/$ID request handling + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_CATEGORIES_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_CATEGORIES_ID_H + +#include "taler-merchant-httpd.h" + + +/** + * PATCH descriptions of an existing product category. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_patch_categories_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/backend/taler-merchant-httpd_private-patch-instances-ID.c b/src/backend/taler-merchant-httpd_private-patch-instances-ID.c index 027d5869..70df009e 100644 --- a/src/backend/taler-merchant-httpd_private-patch-instances-ID.c +++ b/src/backend/taler-merchant-httpd_private-patch-instances-ID.c @@ -64,17 +64,12 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, { struct TALER_MERCHANTDB_InstanceSettings is; const char *name; - const char *uts = "business"; struct TMH_WireMethod *wm_head = NULL; struct TMH_WireMethod *wm_tail = NULL; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("name", &name), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("user_type", - &uts), - NULL), - GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("website", (const char **) &is.website), NULL), @@ -115,19 +110,6 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, ? MHD_YES : MHD_NO; } - if (NULL == uts) - uts = "business"; - if (GNUNET_OK != - TALER_KYCLOGIC_kyc_user_type_from_string (uts, - &is.ut)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "user_type"); - } if (! TMH_location_object_valid (is.address)) { GNUNET_break_op (0); @@ -185,7 +167,6 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, /* Check for equality of settings */ if (! ( (0 == strcmp (mi->settings.name, name)) && - (mi->settings.ut == is.ut) && ((mi->settings.email == is.email) || (NULL != is.email && NULL != mi->settings.email && 0 == strcmp (mi->settings.email, @@ -240,7 +221,6 @@ giveup: json_decref (mi->settings.jurisdiction); is.id = mi->settings.id; mi->settings = is; - // mi->settings.user_type = (enum TALER_KYCLOGIC_KycUserType) is.user_type; mi->settings.address = json_incref (mi->settings.address); mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction); mi->settings.name = GNUNET_strdup (name); diff --git a/src/backend/taler-merchant-httpd_private-patch-products-ID.c b/src/backend/taler-merchant-httpd_private-patch-products-ID.c index 7bc327cd..6e50cced 100644 --- a/src/backend/taler-merchant-httpd_private-patch-products-ID.c +++ b/src/backend/taler-merchant-httpd_private-patch-products-ID.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2020 Taler Systems SA + (C) 2020-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -29,76 +29,6 @@ /** - * How often do we retry the simple INSERT database transaction? - */ -#define MAX_RETRIES 3 - - -/** - * Determine the cause of the PATCH failure in more detail and report. - * - * @param connection connection to report on - * @param instance_id instance we are processing - * @param product_id ID of the product to patch - * @param pd product details we failed to set - */ -static MHD_RESULT -determine_cause (struct MHD_Connection *connection, - const char *instance_id, - const char *product_id, - const struct TALER_MERCHANTDB_ProductDetails *pd) -{ - struct TALER_MERCHANTDB_ProductDetails pdx; - enum GNUNET_DB_QueryStatus qs; - - qs = TMH_db->lookup_product (TMH_db->cls, - instance_id, - product_id, - &pdx); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "unexpected serialization problem"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN, - product_id); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; /* do below */ - } - - { - enum TALER_ErrorCode ec; - - ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - if (pdx.total_lost > pd->total_lost) - ec = TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_REDUCED; - if (pdx.total_sold > pd->total_sold) - ec = TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_SOLD_REDUCED; - if (pdx.total_stock > pd->total_stock) - ec = TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_STOCKED_REDUCED; - TALER_MERCHANTDB_product_details_free (&pdx); - GNUNET_break (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE != ec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - ec, - NULL); - } -} - - -/** * PATCH configuration of an existing instance, given its configuration. * * @param rh context of the handler @@ -107,13 +37,15 @@ determine_cause (struct MHD_Connection *connection, * @return MHD result code */ MHD_RESULT -TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) +TMH_private_patch_products_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { struct TMH_MerchantInstance *mi = hc->instance; const char *product_id = hc->infix; struct TALER_MERCHANTDB_ProductDetails pd = {0}; + const json_t *categories = NULL; int64_t total_stock; enum GNUNET_DB_QueryStatus qs; struct GNUNET_JSON_Specification spec[] = { @@ -135,6 +67,10 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, GNUNET_JSON_spec_json ("taxes", &pd.taxes), NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("categories", + &categories), + NULL), GNUNET_JSON_spec_int64 ("total_stock", &total_stock), GNUNET_JSON_spec_mark_optional ( @@ -155,6 +91,15 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, NULL), GNUNET_JSON_spec_end () }; + MHD_RESULT ret; + size_t num_cats = 0; + uint64_t *cats = NULL; + bool no_instance; + ssize_t no_cat; + bool no_product; + bool lost_reduced; + bool sold_reduced; + bool stock_reduced; pd.total_sold = 0; /* will be ignored anyway */ GNUNET_assert (NULL != mi); @@ -173,11 +118,11 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, if (total_stock < -1) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "total_stock"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "total_stock"); + goto cleanup; } if (-1 == total_stock) pd.total_stock = INT64_MAX; @@ -189,23 +134,45 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, if (! TMH_location_object_valid (pd.address)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "address"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "address"); + goto cleanup; + } + num_cats = json_array_size (categories); + cats = GNUNET_new_array (num_cats, + uint64_t); + { + size_t idx; + json_t *val; + + json_array_foreach (categories, idx, val) + { + if (! json_is_integer (val)) + { + GNUNET_break_op (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "categories"); + goto cleanup; + } + cats[idx] = json_integer_value (val); + } } + if (NULL == pd.description_i18n) pd.description_i18n = json_object (); if (! TALER_JSON_check_i18n (pd.description_i18n)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "description_i18n"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "description_i18n"); + goto cleanup; } if (NULL == pd.taxes) @@ -214,74 +181,143 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, if (! TMH_taxes_array_valid (pd.taxes)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "taxes"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "taxes"); + goto cleanup; } + if (NULL == pd.image) pd.image = ""; if (! TMH_image_data_url_valid (pd.image)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "image"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "image"); + goto cleanup; } + if ( (pd.total_stock < pd.total_sold + pd.total_lost) || (pd.total_sold + pd.total_lost < pd.total_sold) /* integer overflow */) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( + ret = TALER_MHD_reply_with_error ( connection, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_EXCEEDS_STOCKS, NULL); + goto cleanup; } + qs = TMH_db->update_product (TMH_db->cls, mi->settings.id, product_id, - &pd); + &pd, + num_cats, + cats, + &no_instance, + &no_cat, + &no_product, + &lost_reduced, + &sold_reduced, + &stock_reduced); + switch (qs) { - MHD_RESULT ret = MHD_NO; + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + NULL); + goto cleanup; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "unexpected serialization problem"); + goto cleanup; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "unexpected problem in stored procedure"); + goto cleanup; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - NULL); - break; - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "unexpected serialization problem"); - break; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - ret = determine_cause (connection, - mi->settings.id, - product_id, - &pd); - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - ret = TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - break; - } - GNUNET_JSON_parse_free (spec); - return ret; + if (no_instance) + { + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, + mi->settings.id); + goto cleanup; + } + if (-1 != no_cat) + { + char cats[24]; + + GNUNET_snprintf (cats, + sizeof (cats), + "%llu", + (unsigned long long) no_cat); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_CATEGORY_UNKNOWN, + cats); + goto cleanup; + } + if (no_product) + { + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN, + product_id); + goto cleanup; + } + if (lost_reduced) + { + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_REDUCED, + NULL); + goto cleanup; + } + if (sold_reduced) + { + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_SOLD_REDUCED, + NULL); + goto cleanup; + } + if (stock_reduced) + { + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_STOCKED_REDUCED, + NULL); + goto cleanup; } + /* success! */ + ret = TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); +cleanup: + GNUNET_free (cats); + GNUNET_JSON_parse_free (spec); + return ret; } diff --git a/src/backend/taler-merchant-httpd_private-post-categories.c b/src/backend/taler-merchant-httpd_private-post-categories.c new file mode 100644 index 00000000..675de765 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-categories.c @@ -0,0 +1,170 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + 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 General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-post-categories.c + * @brief implementing POST /private/categories request handling + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-post-categories.h" +#include "taler-merchant-httpd_helper.h" +#include <taler/taler_json_lib.h> + + +/** + * How often do we retry the simple INSERT database transaction? + */ +#define MAX_RETRIES 3 + + +MHD_RESULT +TMH_private_post_categories (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + const char *category_name; + const json_t *category_name_i18n; + uint64_t category_id; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("name", + &category_name), + GNUNET_JSON_spec_object_const ("name_i18n", + &category_name_i18n), + GNUNET_JSON_spec_end () + }; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_assert (NULL != mi); + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + } + + /* finally, interact with DB until no serialization error */ + for (unsigned int i = 0; i<MAX_RETRIES; i++) + { + json_t *xcategory_name_i18n; + + if (GNUNET_OK != + TMH_db->start (TMH_db->cls, + "POST /categories")) + { + GNUNET_break (0); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_START_FAILED, + NULL); + } + qs = TMH_db->select_category_by_name (TMH_db->cls, + mi->settings.id, + category_name, + &xcategory_name_i18n, + &category_id); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + /* Clean up and fail hard */ + GNUNET_break (0); + TMH_db->rollback (TMH_db->cls); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SOFT_ERROR: + /* restart transaction */ + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* Good, we can proceed! */ + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* idempotency check: is etp == tp? */ + { + bool eq; + + eq = (1 == json_equal (xcategory_name_i18n, + category_name_i18n)); + json_decref (xcategory_name_i18n); + TMH_db->rollback (TMH_db->cls); + GNUNET_JSON_parse_free (spec); + return eq + ? TALER_MHD_REPLY_JSON_PACK (connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_uint64 ("category_id", + category_id)) + : TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_CATEGORIES_CONFLICT_CATEGORY_EXISTS, + category_name); + } + } /* end switch (qs) */ + + qs = TMH_db->insert_category (TMH_db->cls, + mi->settings.id, + category_name, + category_name_i18n, + &category_id); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + TMH_db->rollback (TMH_db->cls); + break; + } + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + { + qs = TMH_db->commit (TMH_db->cls); + if (GNUNET_DB_STATUS_SOFT_ERROR != qs) + break; + } +retry: + GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs); + TMH_db->rollback (TMH_db->cls); + } /* for RETRIES loop */ + GNUNET_JSON_parse_free (spec); + if (qs < 0) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + (GNUNET_DB_STATUS_SOFT_ERROR == qs) + ? TALER_EC_GENERIC_DB_SOFT_FAILURE + : TALER_EC_GENERIC_DB_COMMIT_FAILED, + NULL); + } + return TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_uint64 ("category_id", + category_id)); +} + + +/* end of taler-merchant-httpd_private-post-categories.c */ diff --git a/src/backend/taler-merchant-httpd_private-post-categories.h b/src/backend/taler-merchant-httpd_private-post-categories.h new file mode 100644 index 00000000..a8431059 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-categories.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + 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 General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-post-categories.h + * @brief implementing POST /categories request handling + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_CATEGORIES_H +#define TALER_MERCHANT_HTTPD_PRIVATE_POST_CATEGORIES_H + +#include "taler-merchant-httpd.h" + + +/** + * Generate a product category. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_post_categories ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/backend/taler-merchant-httpd_private-post-instances.c b/src/backend/taler-merchant-httpd_private-post-instances.c index a4cf884d..f56e7098 100644 --- a/src/backend/taler-merchant-httpd_private-post-instances.c +++ b/src/backend/taler-merchant-httpd_private-post-instances.c @@ -103,20 +103,6 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, ? MHD_YES : MHD_NO; } - if (NULL == uts) - uts = "business"; - if (GNUNET_OK != - TALER_KYCLOGIC_kyc_user_type_from_string (uts, - &is.ut)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "user_type"); - } - { enum GNUNET_GenericReturnValue ret; diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index e8332c70..a2e3b493 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -960,6 +960,8 @@ execute_order (struct OrderContext *oc) struct TALER_MERCHANTDB_ProductDetails pd; MHD_RESULT ret; const struct InventoryProduct *ip; + size_t num_categories = 0; + uint64_t *categories = NULL; ip = &oc->parse_request.inventory_products[ oc->execute_order.out_of_stock_index]; @@ -970,10 +972,13 @@ execute_order (struct OrderContext *oc) TMH_db->cls, oc->hc->instance->settings.id, ip->product_id, - &pd); + &pd, + &num_categories, + &categories); switch (qs) { case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + GNUNET_free (categories); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Order creation failed: product out of stock\n"); ret = TALER_MHD_REPLY_JSON_PACK ( @@ -1337,6 +1342,7 @@ get_exchange_keys (void *cls, rx); } + /** * Get rounded time interval. @a start is calculated by rounding * @a ts down to the nearest multiple of @a precision. @a end is @@ -1449,9 +1455,7 @@ set_token_family (struct OrderContext *oc, struct TALER_MERCHANTDB_TokenFamilyKeyDetails key_details; struct TALER_MerchantContractTokenFamily *family = NULL; enum GNUNET_DB_QueryStatus qs; - // TODO: make this configurable. This is the granularity of token - // expiration dates. This should be stored in the - // database along the token family. + /* TODO: Implement rounding duration of token family and use this here. */ struct GNUNET_TIME_Relative precision = GNUNET_TIME_UNIT_MONTHS; struct GNUNET_TIME_Timestamp min_valid_after; struct GNUNET_TIME_Timestamp max_valid_after; @@ -1694,6 +1698,7 @@ set_token_family (struct OrderContext *oc, return GNUNET_OK; } + /** * Serialize order into @a oc->serialize_order.contract, * ready to be stored in the database. Upon success, continue @@ -1712,16 +1717,16 @@ serialize_order (struct OrderContext *oc) merchant = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("name", - settings->name), + settings->name), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("website", - settings->website)), + settings->website)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("email", - settings->email)), + settings->email)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("logo", - settings->logo))); + settings->logo))); GNUNET_assert (NULL != merchant); { json_t *loca; @@ -1733,7 +1738,7 @@ serialize_order (struct OrderContext *oc) loca = json_deep_copy (loca); GNUNET_assert (NULL != loca); GNUNET_assert (0 == - json_object_set_new (merchant, + json_object_set_new (merchant, "address", loca)); } @@ -1748,7 +1753,7 @@ serialize_order (struct OrderContext *oc) juri = json_deep_copy (juri); GNUNET_assert (NULL != juri); GNUNET_assert (0 == - json_object_set_new (merchant, + json_object_set_new (merchant, "jurisdiction", juri)); } @@ -1855,10 +1860,10 @@ serialize_order (struct OrderContext *oc) json_t *jchoice = GNUNET_JSON_PACK ( GNUNET_JSON_pack_array_incref ("inputs", - inputs), + inputs), GNUNET_JSON_pack_array_incref ("outputs", - outputs) - ); + outputs) + ); GNUNET_assert (0 == json_array_append (choices, jchoice)); } @@ -1879,7 +1884,8 @@ serialize_order (struct OrderContext *oc) oc->parse_order.fulfillment_message)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("fulfillment_message_i18n", - oc->parse_order.fulfillment_message_i18n)), + oc->parse_order.fulfillment_message_i18n)) + , GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("fulfillment_url", oc->parse_order.fulfillment_url)), @@ -1961,6 +1967,7 @@ serialize_order (struct OrderContext *oc) oc->phase++; } + /** * Set max_fee in @a oc based on STEFAN value if * not yet present. Upon success, continue @@ -1996,6 +2003,7 @@ set_max_fee (struct OrderContext *oc) oc->phase++; } + /** * Set list of acceptable exchanges in @a oc. Upon success, continue * processing with set_max_fee(). @@ -2119,7 +2127,7 @@ parse_order (struct OrderContext *oc) NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_array_const ("choices", - &oc->parse_order.choices), + &oc->parse_order.choices), NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_web_url ("merchant_base_url", @@ -2185,7 +2193,7 @@ parse_order (struct OrderContext *oc) ret); return; } - if (NULL == version || 0 == strcmp("0", version)) + if (NULL == version || 0 == strcmp ("0", version)) { oc->parse_order.version = TALER_MCV_V0; @@ -2200,11 +2208,11 @@ parse_order (struct OrderContext *oc) return; } } - else if (0 == strcmp("1", version)) + else if (0 == strcmp ("1", version)) { oc->parse_order.version = TALER_MCV_V1; - if (! json_is_array(oc->parse_order.choices)) + if (! json_is_array (oc->parse_order.choices)) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); @@ -2237,9 +2245,9 @@ parse_order (struct OrderContext *oc) return; } if ( (! no_fee) && - (GNUNET_OK != - TALER_amount_cmp_currency (&oc->parse_order.brutto, - &oc->parse_order.max_fee)) ) + (GNUNET_OK != + TALER_amount_cmp_currency (&oc->parse_order.brutto, + &oc->parse_order.max_fee)) ) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); @@ -2516,6 +2524,7 @@ parse_order (struct OrderContext *oc) oc->phase++; } + /** * Parse contract choices. Upon success, continue * processing with merge_inventory(). @@ -2606,7 +2615,7 @@ parse_choices (struct OrderContext *oc) GNUNET_JSON_spec_uint32 ("count", &input.details.token.count), NULL), - GNUNET_JSON_spec_end() + GNUNET_JSON_spec_end () }; if (GNUNET_OK != @@ -2665,7 +2674,8 @@ parse_choices (struct OrderContext *oc) size_t idx; json_array_foreach ((json_t *) joutputs, idx, joutput) { - struct TALER_MerchantContractOutput output = { .details.token.count = 1 }; + struct TALER_MerchantContractOutput output = { .details.token.count = 1} + ; const char *kind; const char *ierror_name; unsigned int ierror_line; @@ -2682,14 +2692,14 @@ parse_choices (struct OrderContext *oc) GNUNET_JSON_spec_uint32 ("count", &output.details.token.count), NULL), - GNUNET_JSON_spec_end() + GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (joutput, - ispec, - &ierror_name, - &ierror_line)) + ispec, + &ierror_name, + &ierror_line)) { GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (ispec); @@ -2744,6 +2754,7 @@ parse_choices (struct OrderContext *oc) oc->phase++; } + /** * Process the @a payment_target and add the details of how the * order could be paid to @a order. On success, continue @@ -2809,11 +2820,15 @@ merge_inventory (struct OrderContext *oc) = &oc->parse_request.inventory_products[i]; struct TALER_MERCHANTDB_ProductDetails pd; enum GNUNET_DB_QueryStatus qs; + size_t num_categories = 0; + uint64_t *categories = NULL; qs = TMH_db->lookup_product (TMH_db->cls, oc->hc->instance->settings.id, ip->product_id, - &pd); + &pd, + &num_categories, + &categories); if (qs <= 0) { enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; @@ -2848,6 +2863,7 @@ merge_inventory (struct OrderContext *oc) ip->product_id); return; } + GNUNET_free (categories); oc->parse_order.minimum_age = GNUNET_MAX (oc->parse_order.minimum_age, pd.minimum_age); diff --git a/src/backend/taler-merchant-httpd_private-post-products-ID-lock.c b/src/backend/taler-merchant-httpd_private-post-products-ID-lock.c index 184f1d28..844b2ec8 100644 --- a/src/backend/taler-merchant-httpd_private-post-products-ID-lock.c +++ b/src/backend/taler-merchant-httpd_private-post-products-ID-lock.c @@ -29,9 +29,10 @@ MHD_RESULT -TMH_private_post_products_ID_lock (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) +TMH_private_post_products_ID_lock ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { struct TMH_MerchantInstance *mi = hc->instance; const char *product_id = hc->infix; @@ -75,35 +76,48 @@ TMH_private_post_products_ID_lock (const struct TMH_RequestHandler *rh, switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - NULL); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + NULL); case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "Serialization error for single-statment request"); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "Serialization error for single-statment request"); case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - qs = TMH_db->lookup_product (TMH_db->cls, - mi->settings.id, - product_id, - NULL); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "lookup_product"); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN, - product_id); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_GONE, - TALER_EC_MERCHANT_PRIVATE_POST_PRODUCTS_LOCK_INSUFFICIENT_STOCKS, - product_id); + { + size_t num_categories = 0; + uint64_t *categories = NULL; + + qs = TMH_db->lookup_product (TMH_db->cls, + mi->settings.id, + product_id, + NULL, + &num_categories, + &categories); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "lookup_product"); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN, + product_id); + GNUNET_free (categories); + } + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_MERCHANT_PRIVATE_POST_PRODUCTS_LOCK_INSUFFICIENT_STOCKS, + product_id); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: return TALER_MHD_reply_static (connection, MHD_HTTP_NO_CONTENT, diff --git a/src/backend/taler-merchant-httpd_private-post-products.c b/src/backend/taler-merchant-httpd_private-post-products.c index 3cad91a9..3edc0c16 100644 --- a/src/backend/taler-merchant-httpd_private-post-products.c +++ b/src/backend/taler-merchant-httpd_private-post-products.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2020 Taler Systems SA + (C) 2020-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -28,50 +28,6 @@ #include <taler/taler_json_lib.h> -/** - * How often do we retry the simple INSERT database transaction? - */ -#define MAX_RETRIES 3 - - -/** - * Check if the two products are identical. - * - * @param p1 product to compare - * @param p2 other product to compare - * @return true if they are 'equal', false if not or of payto_uris is not an array - */ -static bool -products_equal (const struct TALER_MERCHANTDB_ProductDetails *p1, - const struct TALER_MERCHANTDB_ProductDetails *p2) -{ - return ( (0 == strcmp (p1->description, - p2->description)) && - (1 == json_equal (p1->description_i18n, - p2->description_i18n)) && - (0 == strcmp (p1->unit, - p2->unit)) && - (GNUNET_OK == - TALER_amount_cmp_currency (&p1->price, - &p2->price)) && - (0 == TALER_amount_cmp (&p1->price, - &p2->price)) && - (1 == json_equal (p1->taxes, - p2->taxes)) && - (p1->total_stock == p2->total_stock) && - (p1->total_sold == p2->total_sold) && - (p1->total_lost == p2->total_lost) && - (p1->minimum_age == p2->minimum_age) && - (0 == strcmp (p1->image, - p2->image)) && - (1 == json_equal (p1->address, - p2->address)) && - (GNUNET_TIME_timestamp_cmp (p1->next_restock, - ==, - p2->next_restock) ) ); -} - - MHD_RESULT TMH_private_post_products (const struct TMH_RequestHandler *rh, struct MHD_Connection *connection, @@ -79,9 +35,9 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, { struct TMH_MerchantInstance *mi = hc->instance; struct TALER_MERCHANTDB_ProductDetails pd = { 0 }; + const json_t *categories = NULL; const char *product_id; int64_t total_stock; - enum GNUNET_DB_QueryStatus qs; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("product_id", &product_id), @@ -103,6 +59,10 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, GNUNET_JSON_spec_json ("taxes", &pd.taxes), NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("categories", + &categories), + NULL), GNUNET_JSON_spec_int64 ("total_stock", &total_stock), GNUNET_JSON_spec_mark_optional ( @@ -119,6 +79,13 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, NULL), GNUNET_JSON_spec_end () }; + size_t num_cats = 0; + uint64_t *cats = NULL; + bool conflict; + bool no_instance; + ssize_t no_cat; + enum GNUNET_DB_QueryStatus qs; + MHD_RESULT ret; GNUNET_assert (NULL != mi); { @@ -138,13 +105,33 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, if (total_stock < -1) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "total_stock"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "total_stock"); + goto cleanup; } + num_cats = json_array_size (categories); + cats = GNUNET_new_array (num_cats, + uint64_t); + { + size_t idx; + json_t *val; + json_array_foreach (categories, idx, val) + { + if (! json_is_integer (val)) + { + GNUNET_break_op (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "categories"); + goto cleanup; + } + cats[idx] = json_integer_value (val); + } + } if (-1 == total_stock) pd.total_stock = INT64_MAX; @@ -162,31 +149,31 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, if (! TMH_taxes_array_valid (pd.taxes)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "taxes"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "taxes"); + goto cleanup; } if (! TMH_location_object_valid (pd.address)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "address"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "address"); + goto cleanup; } if (! TALER_JSON_check_i18n (pd.description_i18n)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "description_i18n"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "description_i18n"); + goto cleanup; } if (NULL == pd.image) @@ -194,110 +181,88 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, if (! TMH_image_data_url_valid (pd.image)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "image"); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "image"); + goto cleanup; } - /* finally, interact with DB until no serialization error */ - for (unsigned int i = 0; i<MAX_RETRIES; i++) + qs = TMH_db->insert_product (TMH_db->cls, + mi->settings.id, + product_id, + &pd, + num_cats, + cats, + &no_instance, + &conflict, + &no_cat); + switch (qs) { - /* Test if an product of this id is known */ - struct TALER_MERCHANTDB_ProductDetails epd; - - if (GNUNET_OK != - TMH_db->start (TMH_db->cls, - "/post products")) - { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_START_FAILED, - NULL); - } - qs = TMH_db->lookup_product (TMH_db->cls, - mi->settings.id, - product_id, - &epd); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - /* Clean up and fail hard */ - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SOFT_ERROR: - /* restart transaction */ - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* Good, we can proceed! */ - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* idempotency check: is epd == pd? */ - { - bool eq; - - eq = products_equal (&pd, - &epd); - TALER_MERCHANTDB_product_details_free (&epd); - TMH_db->rollback (TMH_db->cls); - GNUNET_JSON_parse_free (spec); - return eq - ? TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0) - : TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_PRODUCTS_CONFLICT_PRODUCT_EXISTS, - product_id); - } - } /* end switch (qs) */ - - qs = TMH_db->insert_product (TMH_db->cls, - mi->settings.id, - product_id, - &pd); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - TMH_db->rollback (TMH_db->cls); - break; - } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - qs = TMH_db->commit (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; - } -retry: - GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs); - TMH_db->rollback (TMH_db->cls); - } /* for RETRIES loop */ - GNUNET_JSON_parse_free (spec); - if (qs < 0) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error ( + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + ret = TALER_MHD_reply_with_error ( connection, MHD_HTTP_INTERNAL_SERVER_ERROR, (GNUNET_DB_STATUS_SOFT_ERROR == qs) ? TALER_EC_GENERIC_DB_SOFT_FAILURE : TALER_EC_GENERIC_DB_COMMIT_FAILED, NULL); + goto cleanup; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_INVARIANT_FAILURE, + NULL); + goto cleanup; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; } - return TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); + if (no_instance) + { + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, + mi->settings.id); + goto cleanup; + } + if (conflict) + { + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_PRODUCTS_CONFLICT_PRODUCT_EXISTS, + product_id); + goto cleanup; + } + if (-1 != no_cat) + { + char nocats[24]; + + GNUNET_break_op (0); + TMH_db->rollback (TMH_db->cls); + GNUNET_snprintf (nocats, + sizeof (nocats), + "%llu", + (unsigned long long) no_cat); + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_CATEGORY_UNKNOWN, + nocats); + goto cleanup; + } + ret = TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); +cleanup: + GNUNET_JSON_parse_free (spec); + GNUNET_free (cats); + return ret; } diff --git a/src/backend/taler-merchant-webhook.c b/src/backend/taler-merchant-webhook.c index 80db78fd..1d17843e 100644 --- a/src/backend/taler-merchant-webhook.c +++ b/src/backend/taler-merchant-webhook.c @@ -506,7 +506,7 @@ run (void *cls, { GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); - global_ret = EXIT_NO_RESTART; + global_ret = EXIT_FAILURE; return; } if (NULL == @@ -524,7 +524,7 @@ run (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to connect to database\n"); GNUNET_SCHEDULER_shutdown (); - global_ret = EXIT_NO_RESTART; + global_ret = EXIT_FAILURE; return; } { diff --git a/src/backend/taler-merchant-wirewatch.c b/src/backend/taler-merchant-wirewatch.c index 17eb7a0a..bba101e3 100644 --- a/src/backend/taler-merchant-wirewatch.c +++ b/src/backend/taler-merchant-wirewatch.c @@ -641,7 +641,7 @@ run (void *cls, { GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); - global_ret = EXIT_NO_RESTART; + global_ret = EXIT_FAILURE; return; } if (NULL == @@ -659,7 +659,7 @@ run (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to connect to database\n"); GNUNET_SCHEDULER_shutdown (); - global_ret = EXIT_NO_RESTART; + global_ret = EXIT_FAILURE; return; } { |