merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 4c184f7e685817b565013c3afb2a5c30ecf10161
parent 1e257c787a4344f242fd941dbfcdb957c7be4ba0
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 21 Jul 2021 15:29:48 +0200

-fix bugs, FTBFS

Diffstat:
Msrc/backend/taler-merchant-httpd_helper.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/backend/taler-merchant-httpd_helper.h | 21+++++++++++++++++++++
Msrc/backend/taler-merchant-httpd_private-delete-instances-ID.c | 23++++++++++++-----------
Msrc/backend/taler-merchant-httpd_private-delete-products-ID.c | 12++++++++++--
Msrc/backend/taler-merchant-httpd_private-get-products-ID.c | 30++++++++++++++++--------------
Msrc/backend/taler-merchant-httpd_private-patch-products-ID.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/backend/taler-merchant-httpd_private-post-products.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/backenddb/plugin_merchantdb_postgres.c | 85+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
8 files changed, 335 insertions(+), 127 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_helper.c b/src/backend/taler-merchant-httpd_helper.c @@ -80,6 +80,74 @@ TMH_payto_uri_array_valid (const json_t *payto_uris) } +bool +TMH_location_object_valid (const json_t *location) +{ + return true; // FIXME +} + + +// FIXME +bool +TMH_i18n_object_valid (const json_t *i18n) +{ + return true; // FIXME +} + + +// FIXME +bool +TMH_image_data_url_valid (const char *image_data_url) +{ + if (0 == strcmp (image_data_url, + "")) + return true; + if (0 != strncasecmp ("data:image/", + image_data_url, + strlen ("data:image/"))) + return false; + if (NULL == strstr (";base64,", + image_data_url)) + return false; + // FIXME: write generic URI syntax validation */ + return true; +} + + +bool +TMH_taxes_array_valid (const json_t *taxes) +{ + json_t *tax; + size_t idx; + + if (! json_is_array (taxes)) + return false; + json_array_foreach (taxes, idx, tax) + { + struct TALER_Amount amount; + const char *name; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("name", + &name), + TALER_JSON_spec_amount ("tax", + &amount), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (NULL, + tax, + spec); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + return false; + } + } + return true; +} + + struct TMH_WireMethod * TMH_setup_wire_account (const char *payto_uri) { diff --git a/src/backend/taler-merchant-httpd_helper.h b/src/backend/taler-merchant-httpd_helper.h @@ -38,6 +38,27 @@ TMH_payto_uri_array_valid (const json_t *payto_uris); /** + * FIXME. + */ +bool +TMH_taxes_array_valid (const json_t *taxes); + + +// FIXME +bool +TMH_location_object_valid (const json_t *location); + + +// FIXME +bool +TMH_i18n_object_valid (const json_t *i18n); + + +// FIXME +bool +TMH_image_data_url_valid (const char *image_data_url); + +/** * Setup new wire method for the given @ payto_uri. * * @param payto_uri already validated payto URI diff --git a/src/backend/taler-merchant-httpd_private-delete-instances-ID.c b/src/backend/taler-merchant-httpd_private-delete-instances-ID.c @@ -34,16 +34,19 @@ static MHD_RESULT delete_instances_ID (struct TMH_MerchantInstance *mi, struct MHD_Connection *connection) { - const char *purge; + const char *purge_s; + bool purge; enum GNUNET_DB_QueryStatus qs; GNUNET_assert (NULL != mi); - purge = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "purge"); - if ( (NULL != purge) && - (0 == strcmp (purge, - "yes")) ) + purge_s = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "purge"); + if (NULL == purge_s) + purge_s = "no"; + purge = (0 == strcmp (purge_s, + "yes")); + if (purge) qs = TMH_db->purge_instance (TMH_db->cls, mi->settings.id); else @@ -66,13 +69,11 @@ delete_instances_ID (struct TMH_MerchantInstance *mi, return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, - ( (NULL != purge) && - (0 == strcmp (purge, - "yes")) ) + purge ? "Instance unknown" : "Private key unknown"); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - if (NULL != purge) + if (purge) TMH_instance_decref (mi); else mi->deleted = true; diff --git a/src/backend/taler-merchant-httpd_private-delete-products-ID.c b/src/backend/taler-merchant-httpd_private-delete-products-ID.c @@ -40,6 +40,7 @@ TMH_private_delete_products_ID (const struct TMH_RequestHandler *rh, enum GNUNET_DB_QueryStatus qs; GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != hc->infix); qs = TMH_db->delete_product (TMH_db->cls, mi->settings.id, hc->infix); @@ -49,18 +50,25 @@ TMH_private_delete_products_ID (const struct TMH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_STORE_FAILED, - NULL); + "delete_product"); 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, - NULL); + "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, diff --git a/src/backend/taler-merchant-httpd_private-get-products-ID.c b/src/backend/taler-merchant-httpd_private-get-products-ID.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2019, 2020 Taler Systems SA + (C) 2019, 2020, 2021 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 @@ -51,28 +51,32 @@ TMH_private_get_products_ID (const struct TMH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); + "lookup_product"); } - else if (0 == qs) + if (0 == qs) { return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN, - NULL); + hc->infix); } { json_t *reply; MHD_RESULT ret; reply = json_pack ( - "{s:s, s:s, s:o, s:o, s:I," - " s:I, s:I, s:o, s:o, s:s}", + "{s:s, s:o, s:s, s:o, s:o," + " s:o, s:I, s:I, s:I, s:o}", "description", pd.description, + "description_i18n", + pd.description_i18n, "unit", pd.unit, "price", TALER_JSON_from_amount (&pd.price), + "image", + pd.image, "taxes", pd.taxes, "total_stock", @@ -84,18 +88,16 @@ TMH_private_get_products_ID (const struct TMH_RequestHandler *rh, (json_int_t) pd.total_sold, "total_lost", (json_int_t) pd.total_lost, - "description_i18n", - pd.description_i18n, "address", - pd.address, - "image", - pd.image); + pd.address); GNUNET_free (pd.description); GNUNET_free (pd.unit); if (0 != pd.next_restock.abs_value_us) - json_object_set_new (reply, - "next_restock", - GNUNET_JSON_from_time_abs (pd.next_restock)); + GNUNET_assert (0 == + json_object_set_new ( + reply, + "next_restock", + GNUNET_JSON_from_time_abs (pd.next_restock))); ret = TALER_MHD_reply_json (connection, reply, MHD_HTTP_OK); diff --git a/src/backend/taler-merchant-httpd_private-patch-products-ID.c b/src/backend/taler-merchant-httpd_private-patch-products-ID.c @@ -24,6 +24,7 @@ */ #include "platform.h" #include "taler-merchant-httpd_private-patch-products-ID.h" +#include "taler-merchant-httpd_helper.h" #include <taler/taler_json_lib.h> @@ -87,8 +88,6 @@ determine_cause (struct MHD_Connection *connection, 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; - if (pd->total_stock < pd->total_sold + pd->total_lost) - ec = TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_EXCEEDS_STOCKS; TALER_MERCHANTDB_product_details_free (&pdx); GNUNET_break (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE != ec); return TALER_MHD_reply_with_error (connection, @@ -133,15 +132,15 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("taxes", &pd.taxes)), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("address", - &pd.address)), GNUNET_JSON_spec_int64 ("total_stock", &total_stock), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_uint64 ("total_lost", &pd.total_lost)), GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("address", + &pd.address)), + GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_absolute_time ("next_restock", &pd.next_restock)), GNUNET_JSON_spec_end () @@ -170,7 +169,16 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_CONFLICT, TALER_EC_GENERIC_CURRENCY_MISMATCH, - NULL); + TMH_currency); + } + 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"); } if (-1 == total_stock) pd.total_stock = INT64_MAX; @@ -178,35 +186,63 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, pd.total_stock = (uint64_t) total_stock; if (NULL == pd.address) pd.address = json_object (); + + 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"); + } if (NULL == pd.description_i18n) pd.description_i18n = json_object (); + + if (! TMH_i18n_object_valid (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"); + } + if (NULL == pd.taxes) - pd.taxes = json_object (); + pd.taxes = json_array (); + /* check taxes is well-formed */ + 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"); + } if (NULL == pd.image) pd.image = ""; - if (NULL != json_object_get (hc->request_body, - "next_restock")) + if (! TMH_image_data_url_valid (pd.image)) { - enum GNUNET_GenericReturnValue res; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_absolute_time ("next_restock", - &pd.next_restock), - GNUNET_JSON_spec_end () - }; - - res = TALER_MHD_parse_json_data (connection, - hc->request_body, - spec); - if (GNUNET_OK != res) - return (GNUNET_NO == res) - ? MHD_YES - : MHD_NO; + 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"); } - else + if ( (pd.total_stock < pd.total_sold + pd.total_lost) || + (pd.total_sold + pd.total_lost < pd.total_sold) /* integer overflow */) { - pd.next_restock.abs_value_us = 0; + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_EXCEEDS_STOCKS, + NULL); } - qs = TMH_db->update_product (TMH_db->cls, mi->settings.id, product_id, diff --git a/src/backend/taler-merchant-httpd_private-post-products.c b/src/backend/taler-merchant-httpd_private-post-products.c @@ -24,6 +24,7 @@ */ #include "platform.h" #include "taler-merchant-httpd_private-post-products.h" +#include "taler-merchant-httpd_helper.h" #include <taler/taler_json_lib.h> @@ -50,8 +51,9 @@ products_equal (const struct TALER_MERCHANTDB_ProductDetails *p1, p2->description_i18n)) && (0 == strcmp (p1->unit, p2->unit)) && - (GNUNET_OK == TALER_amount_cmp_currency (&p1->price, - &p2->price)) && + (GNUNET_OK == + TALER_amount_cmp_currency (&p1->price, + &p2->price)) && (0 == TALER_amount_cmp (&p1->price, &p2->price)) && (1 == json_equal (p1->taxes, @@ -86,9 +88,6 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("description_i18n", &pd.description_i18n)), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_absolute_time ("next_restock", - &pd.next_restock)), GNUNET_JSON_spec_string ("unit", (const char **) &pd.unit), TALER_JSON_spec_amount ("price", @@ -99,11 +98,11 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("taxes", &pd.taxes)), + GNUNET_JSON_spec_int64 ("total_stock", + &total_stock), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("address", &pd.address)), - GNUNET_JSON_spec_int64 ("total_stock", - &total_stock), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_absolute_time ("next_restock", &pd.next_restock)), @@ -134,8 +133,19 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_CONFLICT, TALER_EC_GENERIC_CURRENCY_MISMATCH, - NULL); + TMH_currency); + } + 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"); } + + if (-1 == total_stock) pd.total_stock = INT64_MAX; else @@ -146,9 +156,52 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, if (NULL == pd.description_i18n) pd.description_i18n = json_object (); if (NULL == pd.taxes) - pd.taxes = json_object (); + pd.taxes = json_array (); + + /* check taxes is well-formed */ + 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"); + } + + 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"); + } + + if (! TMH_i18n_object_valid (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"); + } + 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"); + } + + /* finally, interact with DB until no serialization error */ for (unsigned int i = 0; i<MAX_RETRIES; i++) { /* Test if an product of this id is known */ @@ -173,7 +226,13 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, { case GNUNET_DB_STATUS_HARD_ERROR: /* Clean up and fail hard */ - break; + 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; @@ -182,50 +241,50 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, break; case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: /* idempotency check: is epd == pd? */ - if (products_equal (&pd, - &epd)) - { - TALER_MERCHANTDB_product_details_free (&epd); - TMH_db->rollback (TMH_db->cls); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - } - else { + 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 TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_PRODUCTS_CONFLICT_PRODUCT_EXISTS, - product_id); + 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_SOFT_ERROR == qs) - goto retry; - qs = TMH_db->commit (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; -retry: - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + if (GNUNET_DB_STATUS_HARD_ERROR == qs) { TMH_db->rollback (TMH_db->cls); - continue; + 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_op (0); + GNUNET_break (0); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_INTERNAL_SERVER_ERROR, diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c @@ -1000,42 +1000,53 @@ postgres_lookup_product (void *cls, GNUNET_PQ_query_param_string (product_id), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("description", - &pd->description), - TALER_PQ_result_spec_json ("description_i18n", - &pd->description_i18n), - GNUNET_PQ_result_spec_string ("unit", - &pd->unit), - TALER_PQ_RESULT_SPEC_AMOUNT ("price", - &pd->price), - TALER_PQ_result_spec_json ("taxes", - &pd->taxes), - GNUNET_PQ_result_spec_uint64 ("total_stock", - &pd->total_stock), - GNUNET_PQ_result_spec_uint64 ("total_sold", - &pd->total_sold), - GNUNET_PQ_result_spec_uint64 ("total_lost", - &pd->total_lost), - GNUNET_PQ_result_spec_string ("image", - &pd->image), - TALER_PQ_result_spec_json ("address", - &pd->address), - GNUNET_PQ_result_spec_absolute_time ("next_restock", - &pd->next_restock), - GNUNET_PQ_result_spec_end - }; - struct GNUNET_PQ_ResultSpec rs_null[] = { - GNUNET_PQ_result_spec_end - }; - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_product", - params, - (NULL == pd) - ? rs_null - : rs); + if (NULL == pd) + { + struct GNUNET_PQ_ResultSpec rs_null[] = { + GNUNET_PQ_result_spec_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "lookup_product", + params, + rs_null); + } + else + { + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("description", + &pd->description), + TALER_PQ_result_spec_json ("description_i18n", + &pd->description_i18n), + GNUNET_PQ_result_spec_string ("unit", + &pd->unit), + TALER_PQ_RESULT_SPEC_AMOUNT ("price", + &pd->price), + TALER_PQ_result_spec_json ("taxes", + &pd->taxes), + GNUNET_PQ_result_spec_uint64 ("total_stock", + &pd->total_stock), + GNUNET_PQ_result_spec_uint64 ("total_sold", + &pd->total_sold), + GNUNET_PQ_result_spec_uint64 ("total_lost", + &pd->total_lost), + GNUNET_PQ_result_spec_string ("image", + &pd->image), + TALER_PQ_result_spec_json ("address", + &pd->address), + GNUNET_PQ_result_spec_absolute_time ("next_restock", + &pd->next_restock), + GNUNET_PQ_result_spec_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "lookup_product", + params, + rs); + } } @@ -1148,7 +1159,9 @@ postgres_update_product (void *cls, GNUNET_PQ_query_param_end }; - if (pd->total_stock < pd->total_lost + pd->total_sold) + if ( (pd->total_stock < pd->total_lost + pd->total_sold) || + (pd->total_lost < pd->total_lost + + pd->total_sold) /* integer overflow */) { GNUNET_break (0); return GNUNET_DB_STATUS_HARD_ERROR;