commit 240c6d73f99db07f479d57c4a15f8236b401ced6
parent b337fd6de7d3eb57b4be5d6cfa93e1c562658528
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Tue, 13 Jan 2026 07:28:38 +0900
fix memory leak
Diffstat:
1 file changed, 1222 insertions(+), 1081 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_post-using-templates.c b/src/backend/taler-merchant-httpd_post-using-templates.c
@@ -30,103 +30,46 @@
#include "taler-merchant-httpd_exchanges.h"
#include <taler/taler_json_lib.h>
-// FIXME: further split template-specific logic to reduce redundancy.
-// Make structs for each phase, so easier cleanup and more readable checks
-// add commented sections like in orders
-
/**
- * Context for inventory template processing.
+ * Item selected from inventory_selection.
*/
-struct InventoryTemplateContext
+struct InventoryTemplateItemContext
{
/**
- * Selected products from inventory_selection.
- */
- struct InventoryTemplateItem
- {
- /**
- * Product ID as referenced in inventory.
- */
- char *product_id;
-
- /**
- * Unit quantity string as provided by the client.
- */
- char *unit_quantity;
-
- /**
- * Parsed integer quantity.
- */
- uint64_t quantity_value;
-
- /**
- * Parsed fractional quantity.
- */
- uint32_t quantity_frac;
-
- /**
- * Product details from the DB (includes price array).
- */
- struct TALER_MERCHANTDB_ProductDetails pd;
-
- /**
- * Categories referenced by the product.
- */
- uint64_t *categories;
-
- /**
- * Length of @e categories.
- */
- size_t num_categories;
- } *items;
-
- /**
- * Length of @e items.
- */
- unsigned int items_len;
-
- /**
- * Selected categories from the template contract.
+ * Product ID as referenced in inventory.
*/
- const json_t *selected_categories;
+ char *product_id;
/**
- * Selected products from the template contract.
+ * Unit quantity string as provided by the client.
*/
- const json_t *selected_products;
+ char *unit_quantity;
/**
- * Whether all products are selectable.
+ * Parsed integer quantity.
*/
- bool selected_all;
+ uint64_t quantity_value;
/**
- * Optional pre-calculated amount provided by the client.
+ * Parsed fractional quantity.
*/
- struct TALER_Amount amount;
+ uint32_t quantity_frac;
/**
- * Optional tip amount.
+ * Product details from the DB (includes price array).
*/
- struct TALER_Amount tip;
+ struct TALER_MERCHANTDB_ProductDetails pd;
/**
- * Currency totals shared across selected products.
- * Without amount.currency
+ * Categories referenced by the product.
*/
- struct InventoryTemplateCurrency
- {
- /**
- * Total amount for this currency (without tips)
- */
- struct TALER_Amount total;
- } *currencies;
+ uint64_t *categories;
/**
- * Length of @e currencies.
+ * Length of @e categories.
*/
- unsigned int currencies_len;
+ size_t num_categories;
};
@@ -136,14 +79,14 @@ struct InventoryTemplateContext
enum UsePhase
{
/**
- * Fetch template details from the database.
- */
- USE_PHASE_LOOKUP_TEMPLATE,
- /**
* Parse request payload into context fields.
*/
USE_PHASE_PARSE_REQUEST,
/**
+ * Fetch template details from the database.
+ */
+ USE_PHASE_LOOKUP_TEMPLATE,
+ /**
* Load DB-backed details needed for verification.
*/
USE_PHASE_DB_FETCH,
@@ -188,113 +131,247 @@ struct UseContext
enum TALER_MERCHANT_TemplateType template_type;
/**
- * Summary override from request, if any.
- */
- const char *summary;
-
- /**
- * Fulfillment URL override from request, if any.
- */
- const char *fulfillment_url;
-
- /**
- * Fulfillment message override from request, if any.
- */
- const char *fulfillment_message;
-
- /**
- * Parsed fields for fixed-order templates.
+ * Information set in the #USE_PHASE_PARSE_REQUEST phase.
*/
struct
{
/**
- * Amount provided by the client.
- */
- struct TALER_Amount amount;
- /**
- * Tip provided by the client.
- */
- struct TALER_Amount tip;
- /**
- * True if @e amount was not provided.
- */
- bool no_amount;
- /**
- * True if @e tip was not provided.
- */
- bool no_tip;
- /**
- * Summary from the template contract.
- */
- const char *tsummary;
- /**
- * Currency from the template contract.
+ * Summary override from request, if any.
*/
- const char *tcurrency;
+ const char *summary;
+
/**
- * Amount from the template contract.
+ * Fulfillment URL override from request, if any.
*/
- struct TALER_Amount tamount;
+ const char *fulfillment_url;
+
/**
- * True if @e tamount was not provided.
+ * Fulfillment message override from request, if any.
*/
- bool no_tamount;
+ const char *fulfillment_message;
+
/**
- * True if no summary override was provided.
+ * Parsed fields for each template type.
*/
- bool no_summary;
- } fixed;
+ union
+ {
+ /**
+ * Parsed fields for fixed-order templates.
+ */
+ struct
+ {
+ /**
+ * Amount provided by the client.
+ */
+ struct TALER_Amount amount;
+ /**
+ * Tip provided by the client.
+ */
+ struct TALER_Amount tip;
+ /**
+ * True if @e amount was not provided.
+ */
+ bool no_amount;
+ /**
+ * True if @e tip was not provided.
+ */
+ bool no_tip;
+ } fixed;
+
+ /**
+ * Parsed fields for inventory templates.
+ */
+ struct
+ {
+ /**
+ * Selected products from inventory_selection.
+ */
+ struct InventoryTemplateItemContext *items;
+
+ /**
+ * Length of @e items.
+ */
+ unsigned int items_len;
+
+ /**
+ * Optional pre-calculated amount provided by the client.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Optional tip amount.
+ */
+ struct TALER_Amount tip;
+
+ /**
+ * True if @e amount was not provided.
+ */
+ bool no_amount;
+ /**
+ * True if @e tip was not provided.
+ */
+ bool no_tip;
+ } inventory;
+ } template;
+
+ } parse_request;
/**
- * Parsed fields for inventory templates.
+ * Information set in the #USE_PHASE_DB_FETCH phase.
*/
struct
{
/**
- * True if @e amount was not provided.
+ * Our template details from the DB.
*/
- bool no_amount;
+ struct TALER_MERCHANTDB_TemplateDetails etp;
+
/**
- * True if @e tip was not provided.
+ * True once @e etp was initialized.
*/
- bool no_tip;
+ bool have_etp;
+
/**
- * Template requires exactly one selection.
+ * Inventory template selections from the contract.
*/
- bool choose_one;
+ struct
+ {
+ /**
+ * Selected categories from the template contract.
+ */
+ const json_t *selected_categories;
+
+ /**
+ * Selected products from the template contract.
+ */
+ const json_t *selected_products;
+
+ /**
+ * Whether all products are selectable.
+ */
+ bool selected_all;
+ } inventory;
+ } db_fetch;
+
+ /**
+ * Information set in the #USE_PHASE_VERIFY phase.
+ */
+ struct
+ {
/**
- * Template allows tips.
+ * Verified fields for each template type.
*/
- bool request_tip;
+ union
+ {
+ /**
+ * Verified fields for fixed-order templates.
+ */
+ struct
+ {
+ /**
+ * Summary from the template contract.
+ */
+ const char *tsummary;
+ /**
+ * Currency from the template contract.
+ */
+ const char *tcurrency;
+ /**
+ * Amount from the template contract.
+ */
+ struct TALER_Amount tamount;
+ /**
+ * True if @e tamount was not provided.
+ */
+ bool no_tamount;
+ /**
+ * True if no summary override was provided.
+ */
+ bool no_summary;
+ } fixed;
+
+ /**
+ * Verified fields for inventory templates.
+ */
+ struct
+ {
+ /**
+ * Template requires exactly one selection.
+ */
+ bool choose_one;
+ /**
+ * Template allows tips.
+ */
+ bool request_tip;
+ /**
+ * Summary from the template contract.
+ */
+ const char *tsummary;
+ /**
+ * True if no summary override was provided.
+ */
+ bool no_summary;
+ } inventory;
+ } template;
+
/**
- * Summary from the template contract.
+ * Per-currency totals across selected products (without tips).
*/
- const char *tsummary;
+ struct TALER_Amount *totals;
+
/**
- * True if no summary override was provided.
+ * Length of @e totals.
*/
- bool no_summary;
- } inventory;
+ unsigned int totals_len;
+ } verify;
+};
- /**
- * Inventory template processing context.
- */
- struct InventoryTemplateContext itc;
- /**
- * Our template details from the DB.
- */
- struct TALER_MERCHANTDB_TemplateDetails etp;
+/**
+ * Clean up inventory items.
+ *
+ * @param items item array to free
+ * @param items_len length of @a items
+ */
+static void
+cleanup_inventory_items (struct InventoryTemplateItemContext **items,
+ unsigned int *items_len)
+{
+ if ((NULL == items) || (NULL == *items))
+ return;
+ for (unsigned int i = 0; i < *items_len; i++)
+ {
+ struct InventoryTemplateItemContext *item = &(*items)[i];
- /**
- * True once @e etp was initialized.
- */
- bool have_etp;
-};
+ GNUNET_free (item->product_id);
+ GNUNET_free (item->unit_quantity);
+ TALER_MERCHANTDB_product_details_free (&item->pd);
+ GNUNET_free (item->categories);
+ }
+ GNUNET_free (*items);
+ *items = NULL;
+ if (NULL != items_len)
+ *items_len = 0;
+}
+/**
+ * Clean up inventory totals.
+ *
+ * @param totals totals array to free
+ * @param totals_len length of @a totals
+ */
static void
-cleanup_inventory_template_context (void *cls);
+cleanup_inventory_totals (struct TALER_Amount **totals,
+ unsigned int *totals_len)
+{
+ if ((NULL == totals) || (NULL == *totals))
+ return;
+ GNUNET_free (*totals);
+ *totals = NULL;
+ if (NULL != totals_len)
+ *totals_len = 0;
+}
/**
@@ -307,8 +384,14 @@ cleanup_use_context (void *cls)
{
struct UseContext *uc = cls;
- TALER_MERCHANTDB_template_details_free (&uc->etp);
- cleanup_inventory_template_context (&uc->itc);
+ TALER_MERCHANTDB_template_details_free (&uc->db_fetch.etp);
+ if (TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART == uc->template_type)
+ {
+ cleanup_inventory_items (&uc->parse_request.template.inventory.items,
+ &uc->parse_request.template.inventory.items_len);
+ cleanup_inventory_totals (&uc->verify.totals,
+ &uc->verify.totals_len);
+ }
if (NULL != uc->ihc.cc)
uc->ihc.cc (uc->ihc.ctx);
GNUNET_free (uc->ihc.infix);
@@ -377,127 +460,577 @@ use_reply_with_error (struct UseContext *uc,
}
-/**
- * Check if a product ID appears in the selected_products list.
- *
- * @param selected_products JSON array of selected product IDs, may be NULL
- * @param product_id product ID to check
- * @return true if the product ID is selected
- */
-static bool
-product_id_selected (const json_t *selected_products,
- const char *product_id)
-{
- const json_t *entry;
- size_t idx;
-
- if (NULL == selected_products)
- return false;
- json_array_foreach ((json_t *) selected_products, idx, entry)
- {
- if (! json_is_string (entry))
- continue;
- if (0 == strcmp (json_string_value (entry),
- product_id))
- return true;
- }
- return false;
-}
-
-
-/**
- * Check if any product category is in the selected_categories list.
- *
- * @param selected_categories JSON array of selected category IDs, may be NULL
- * @param num_categories length of @a categories
- * @param categories list of category IDs for the product
- * @return true if any category matches
- */
-static bool
-categories_selected (const json_t *selected_categories,
- size_t num_categories,
- const uint64_t *categories)
-{
- const json_t *entry;
- size_t idx;
-
- if (NULL == selected_categories)
- return false;
- json_array_foreach ((json_t *) selected_categories, idx, entry)
- {
- uint64_t selected_id;
-
- if (! json_is_integer (entry))
- continue;
- if (0 > json_integer_value (entry))
- continue;
- selected_id = (uint64_t) json_integer_value (entry);
- for (size_t i = 0; i < num_categories; i++)
- {
- if (categories[i] == selected_id)
- return true;
- }
- }
- return false;
-}
-
+/* ***************** USE_PHASE_PARSE_REQUEST **************** */
/**
- * Compute the line total for a product based on quantity.
+ * Parse request data for inventory templates.
*
- * @param unit_price price per unit
- * @param quantity integer quantity
- * @param quantity_frac fractional quantity (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
- * @param[out] line_total resulting line total
+ * @param connection connection to reply on
+ * @param hc handler context
+ * @param uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-compute_line_total (const struct TALER_Amount *unit_price,
- uint64_t quantity,
- uint32_t quantity_frac,
- struct TALER_Amount *line_total)
+parse_using_templates_inventory_request (struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc,
+ struct UseContext *uc)
{
- struct TALER_Amount tmp;
+ if (NULL != uc->ihc.request_body)
+ return GNUNET_OK;
- if (GNUNET_OK !=
- TALER_amount_set_zero (unit_price->currency,
- line_total))
+ const json_t *inventory_selection = NULL;
+
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("summary",
+ &uc->parse_request.summary),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("amount",
+ &uc->parse_request.template.inventory.amount),
+ &uc->parse_request.template.inventory.no_amount),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("tip",
+ &uc->parse_request.template.inventory.tip),
+ &uc->parse_request.template.inventory.no_tip),
+ GNUNET_JSON_spec_array_const ("inventory_selection",
+ &inventory_selection),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (connection,
+ hc->request_body,
+ spec);
+ if (GNUNET_OK != res)
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_line_total: failed to init total for currency %s\n",
- unit_price->currency);
+ GNUNET_break_op (0);
+ use_finalize_parse (uc,
+ res);
return GNUNET_SYSERR;
}
- if (0 != quantity)
+
+ if ( (! uc->parse_request.template.inventory.no_amount) ||
+ (! uc->parse_request.template.inventory.no_tip) )
{
- if (0 >
- TALER_amount_multiply (line_total,
- unit_price,
- (uint32_t) quantity))
+ if (! TMH_test_exchange_configured_for_currency (
+ (! uc->parse_request.template.inventory.no_amount)
+ ? uc->parse_request.template.inventory.amount.currency
+ : uc->parse_request.template.inventory.tip.currency))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_line_total: multiply failed for quantity %lu"
- " in %s\n",
- quantity,
- unit_price->currency);
+ GNUNET_break_op (0);
+ cleanup_inventory_items (&uc->parse_request.template.inventory.items,
+ &uc->parse_request.template.inventory.items_len);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
+ "Currency for amount is not supported by backend");
return GNUNET_SYSERR;
}
}
- if (0 != quantity_frac)
+
+ if (! uc->parse_request.template.inventory.no_tip)
{
- if (0 >
- TALER_amount_multiply (&tmp,
- unit_price,
- quantity_frac))
+ if (! uc->parse_request.template.inventory.no_amount &&
+ (GNUNET_YES !=
+ TALER_amount_cmp_currency (
+ &uc->parse_request.template.inventory.amount,
+ &uc->parse_request.template.inventory.tip)))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_line_total: frac multiply failed for frac %u in %s\n",
- quantity_frac,
- unit_price->currency);
+ GNUNET_break_op (0);
+ cleanup_inventory_items (&uc->parse_request.template.inventory.items,
+ &uc->parse_request.template.inventory.items_len);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
+ "Mismatch of currencies between the amount and tip");
return GNUNET_SYSERR;
}
- TALER_amount_divide (&tmp,
- &tmp,
+ }
+
+ for (size_t i = 0; i < json_array_size (inventory_selection); i++)
+ {
+ const char *product_id;
+ const char *quantity;
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_string ("product_id",
+ &product_id),
+ GNUNET_JSON_spec_string ("quantity",
+ &quantity),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *err_name;
+ unsigned int err_line;
+
+ res = GNUNET_JSON_parse (json_array_get (inventory_selection,
+ i),
+ ispec,
+ &err_name,
+ &err_line);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ cleanup_inventory_items (&uc->parse_request.template.inventory.items,
+ &uc->parse_request.template.inventory.items_len);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "inventory_selection");
+ return GNUNET_SYSERR;
+ }
+
+ {
+ struct InventoryTemplateItemContext item;
+
+ memset (&item,
+ 0,
+ sizeof (item));
+ item.product_id = GNUNET_strdup (product_id);
+ item.unit_quantity = GNUNET_strdup (quantity);
+ GNUNET_array_append (uc->parse_request.template.inventory.items,
+ uc->parse_request.template.inventory.items_len,
+ item);
+ }
+ }
+ if (0 == uc->parse_request.template.inventory.items_len)
+ {
+ GNUNET_break_op (0);
+ cleanup_inventory_items (&uc->parse_request.template.inventory.items,
+ &uc->parse_request.template.inventory.items_len);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MISSING,
+ "inventory_selection");
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse request data for fixed-order templates.
+ *
+ * @param connection connection to reply on
+ * @param hc handler context
+ * @param uc use context
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_using_templates_fixed_request (struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc,
+ struct UseContext *uc)
+{
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("summary",
+ &uc->parse_request.summary),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("amount",
+ &uc->parse_request.template.fixed.amount),
+ &uc->parse_request.template.fixed.no_amount),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("tip",
+ &uc->parse_request.template.fixed.tip),
+ &uc->parse_request.template.fixed.no_tip),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (connection,
+ hc->request_body,
+ spec);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ use_finalize_parse (uc,
+ res);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse request data for paivana templates.
+ *
+ * @param connection connection to reply on
+ * @param hc handler context
+ * @param uc use context
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_using_templates_paivana_request (struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc,
+ struct UseContext *uc)
+{
+ /* TODO: PAIVANA include paivana_id in instantiation flow. */
+ return parse_using_templates_fixed_request (connection,
+ hc,
+ uc);
+}
+
+
+static void
+handle_phase_parse_request (struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc,
+ struct UseContext *uc)
+{
+ if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == uc->template_type)
+ {
+ if (NULL == hc->request_body || ! json_is_object (hc->request_body))
+ {
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "request");
+ return;
+ }
+ uc->phase = USE_PHASE_LOOKUP_TEMPLATE;
+ return;
+ }
+ enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
+
+ // FIXME: share more logic between these, just skip (or run) individual
+ // phases depending on the template type!
+ switch (uc->template_type)
+ {
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ res = parse_using_templates_fixed_request (connection,
+ hc,
+ uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
+ /* TODO: PAIVANA handle paivana-specific instantiation. */
+ res = parse_using_templates_paivana_request (connection,
+ hc,
+ uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
+ res = parse_using_templates_inventory_request (connection,
+ hc,
+ uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
+ GNUNET_break (0);
+ use_reply_with_error (
+ uc,
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "template_contract has unexpected type");
+ break;
+ }
+ if (GNUNET_OK == res)
+ uc->phase = USE_PHASE_DB_FETCH;
+}
+
+
+/* ***************** USE_PHASE_LOOKUP_TEMPLATE **************** */
+
+static void
+handle_phase_lookup_template (struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc,
+ struct UseContext *uc)
+{
+ struct TMH_MerchantInstance *mi = hc->instance;
+ const char *template_id = hc->infix;
+
+ if (! uc->db_fetch.have_etp)
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ // FIXME: include template type in request, fully parse
+ // request before even going into the DB.
+ qs = TMH_db->lookup_template (TMH_db->cls,
+ mi->settings.id,
+ template_id,
+ &uc->db_fetch.etp);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ /* Clean up and fail hard */
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ NULL);
+ break;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ /* this should be impossible (single select) */
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ NULL);
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* template not found! */
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
+ template_id);
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* all good */
+ uc->db_fetch.have_etp = true;
+ break;
+ }
+ if (! uc->db_fetch.have_etp)
+ return;
+ }
+ uc->template_type =
+ TMH_template_type_from_contract (uc->db_fetch.etp.template_contract);
+ if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == uc->template_type)
+ {
+ GNUNET_break (0);
+ use_reply_with_error (
+ uc,
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "template_contract has unexpected type");
+ return;
+ }
+ uc->phase = USE_PHASE_PARSE_REQUEST;
+}
+
+
+/* ***************** USE_PHASE_DB_FETCH **************** */
+
+/**
+ * Fetch DB data for inventory templates.
+ *
+ * @param connection connection to reply on
+ * @param hc handler context
+ * @param uc use context
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+db_fetch_using_templates_inventory (struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc,
+ struct UseContext *uc)
+{
+ struct TMH_MerchantInstance *mi = hc->instance;
+
+ for (unsigned int i = 0; i < uc->parse_request.template.inventory.items_len;
+ i++)
+ {
+ struct InventoryTemplateItemContext *item =
+ &uc->parse_request.template.inventory.items[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,
+ mi->settings.id,
+ item->product_id,
+ &pd,
+ &num_categories,
+ &categories);
+ if (qs <= 0)
+ {
+ enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ unsigned int http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
+ break;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ http_status = MHD_HTTP_NOT_FOUND;
+ ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN;
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ GNUNET_assert (0);
+ }
+ use_reply_with_error (uc,
+ connection,
+ http_status,
+ ec,
+ item->product_id);
+ return GNUNET_SYSERR;
+ }
+
+ item->pd = pd;
+ item->categories = categories;
+ item->num_categories = num_categories;
+ }
+ return GNUNET_OK;
+}
+
+
+static void
+handle_phase_db_fetch (struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc,
+ struct UseContext *uc)
+{
+ enum GNUNET_GenericReturnValue res = GNUNET_OK;
+
+ switch (uc->template_type)
+ {
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
+ res = db_fetch_using_templates_inventory (connection,
+ hc,
+ uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
+ GNUNET_break (0);
+ use_reply_with_error (
+ uc,
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "template_contract has unexpected type");
+ break;
+ }
+ if (GNUNET_OK == res)
+ uc->phase = USE_PHASE_VERIFY;
+}
+
+
+/* Helpers for USE_PHASE_VERIFY. */
+
+/**
+ * Check if a product ID appears in the selected_products list.
+ *
+ * @param selected_products JSON array of selected product IDs, may be NULL
+ * @param product_id product ID to check
+ * @return true if the product ID is selected
+ */
+static bool
+product_id_selected (const json_t *selected_products,
+ const char *product_id)
+{
+ const json_t *entry;
+ size_t idx;
+
+ if (NULL == selected_products)
+ return false;
+ json_array_foreach ((json_t *) selected_products, idx, entry)
+ {
+ if (! json_is_string (entry))
+ continue;
+ if (0 == strcmp (json_string_value (entry),
+ product_id))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Check if any product category is in the selected_categories list.
+ *
+ * @param selected_categories JSON array of selected category IDs, may be NULL
+ * @param num_categories length of @a categories
+ * @param categories list of category IDs for the product
+ * @return true if any category matches
+ */
+static bool
+categories_selected (const json_t *selected_categories,
+ size_t num_categories,
+ const uint64_t *categories)
+{
+ const json_t *entry;
+ size_t idx;
+
+ if (NULL == selected_categories)
+ return false;
+ json_array_foreach ((json_t *) selected_categories, idx, entry)
+ {
+ uint64_t selected_id;
+
+ if (! json_is_integer (entry))
+ continue;
+ if (0 > json_integer_value (entry))
+ continue;
+ selected_id = (uint64_t) json_integer_value (entry);
+ for (size_t i = 0; i < num_categories; i++)
+ {
+ if (categories[i] == selected_id)
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Compute the line total for a product based on quantity.
+ *
+ * @param unit_price price per unit
+ * @param quantity integer quantity
+ * @param quantity_frac fractional quantity (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
+ * @param[out] line_total resulting line total
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+compute_line_total (const struct TALER_Amount *unit_price,
+ uint64_t quantity,
+ uint32_t quantity_frac,
+ struct TALER_Amount *line_total)
+{
+ struct TALER_Amount tmp;
+
+ if (GNUNET_OK !=
+ TALER_amount_set_zero (unit_price->currency,
+ line_total))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_line_total: failed to init total for currency %s\n",
+ unit_price->currency);
+ return GNUNET_SYSERR;
+ }
+ if (0 != quantity)
+ {
+ if (0 >
+ TALER_amount_multiply (line_total,
+ unit_price,
+ (uint32_t) quantity))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_line_total: multiply failed for quantity %lu"
+ " in %s\n",
+ quantity,
+ unit_price->currency);
+ return GNUNET_SYSERR;
+ }
+ }
+ if (0 != quantity_frac)
+ {
+ if (0 >
+ TALER_amount_multiply (&tmp,
+ unit_price,
+ quantity_frac))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_line_total: frac multiply failed for frac %u in %s\n",
+ quantity_frac,
+ unit_price->currency);
+ return GNUNET_SYSERR;
+ }
+ TALER_amount_divide (&tmp,
+ &tmp,
TALER_MERCHANT_UNIT_FRAC_BASE);
if (0 >
TALER_amount_add (line_total,
@@ -515,82 +1048,56 @@ compute_line_total (const struct TALER_Amount *unit_price,
/**
- * Clean up a `struct InventoryTemplateContext *`.
- *
- * @param cls a `struct InventoryTemplateContext *`
- */
-static void
-cleanup_inventory_template_context (void *cls)
-{
- struct InventoryTemplateContext *ctx = cls;
-
- if (NULL == ctx)
- return;
- for (unsigned int i = 0; i < ctx->items_len; i++)
- {
- struct InventoryTemplateItem *item = &ctx->items[i];
-
- GNUNET_free (item->product_id);
- GNUNET_free (item->unit_quantity);
- TALER_MERCHANTDB_product_details_free (&item->pd);
- GNUNET_free (item->categories);
- }
- GNUNET_free (ctx->items);
- GNUNET_free (ctx->currencies);
- memset (ctx,
- 0,
- sizeof (*ctx));
-}
-
-
-/**
* Compute totals for all currencies shared across selected products.
*
- * @param[in,out] ctx inventory template context
+ * @param[in,out] uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-compute_totals_per_currency (struct InventoryTemplateContext *ctx)
+compute_totals_per_currency (struct UseContext *uc)
{
struct TALER_Amount line_total;
+ struct InventoryTemplateItemContext *items =
+ uc->parse_request.template.inventory.items;
+ unsigned int items_len = uc->parse_request.template.inventory.items_len;
- if (0 == ctx->items_len)
+ if (0 == items_len)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
- for (size_t i = 0; i < ctx->items[0].pd.price_array_length; i++)
+ for (size_t i = 0; i < items[0].pd.price_array_length; i++)
{
- const char *currency = ctx->items[0].pd.price_array[i].currency;
+ const char *currency = items[0].pd.price_array[i].currency;
if (! TMH_test_exchange_configured_for_currency (currency))
continue;
- GNUNET_array_append (ctx->currencies,
- ctx->currencies_len,
- (struct InventoryTemplateCurrency) { 0 });
+ GNUNET_array_append (uc->verify.totals,
+ uc->verify.totals_len,
+ (struct TALER_Amount) { 0 });
GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (currency,
- &ctx->currencies[ctx->currencies_len
- - 1].total));
+ TALER_amount_set_zero (
+ currency,
+ &uc->verify.totals[uc->verify.totals_len - 1]));
}
- if (0 == ctx->currencies_len)
+ if (0 == uc->verify.totals_len)
return GNUNET_OK;
/* Loop through items, ensure each currency exists and sum totals. */
- for (unsigned int i = 0; i < ctx->items_len; i++)
+ for (unsigned int i = 0; i < items_len; i++)
{
- const struct InventoryTemplateItem *item = &ctx->items[i];
- for (unsigned int c = 0; c < ctx->currencies_len;)
+ const struct InventoryTemplateItemContext *item = &items[i];
+ for (unsigned int c = 0; c < uc->verify.totals_len;)
{
const struct TALER_Amount *unit_price = NULL;
for (size_t j = 0; j < item->pd.price_array_length; j++)
{
if (0 == strcmp (item->pd.price_array[j].currency,
- ctx->currencies[c].total.currency))
+ uc->verify.totals[c].currency))
{
unit_price = &item->pd.price_array[j];
break;
@@ -599,8 +1106,8 @@ compute_totals_per_currency (struct InventoryTemplateContext *ctx)
if (NULL == unit_price)
{
/* Just drop the currency, that we can't find in one of the items*/
- ctx->currencies[c] = ctx->currencies[ctx->currencies_len - 1];
- ctx->currencies_len--;
+ uc->verify.totals[c] = uc->verify.totals[uc->verify.totals_len - 1];
+ uc->verify.totals_len--;
continue;
}
if (GNUNET_OK !=
@@ -610,15 +1117,15 @@ compute_totals_per_currency (struct InventoryTemplateContext *ctx)
&line_total))
return GNUNET_SYSERR;
if (0 >
- TALER_amount_add (&ctx->currencies[c].total,
- &ctx->currencies[c].total,
+ TALER_amount_add (&uc->verify.totals[c],
+ &uc->verify.totals[c],
&line_total))
return GNUNET_SYSERR;
c++;
}
}
- return (0 == ctx->currencies_len)
+ return (0 == uc->verify.totals_len)
? GNUNET_SYSERR // FIXME: at least some logging would be nice...
: GNUNET_OK;
}
@@ -626,296 +1133,89 @@ compute_totals_per_currency (struct InventoryTemplateContext *ctx)
/**
* Compute total for a specific currency.
- *
- * @param[in] ctx inventory template context
- * @param[in] currency currency to total
- * @param[out] total computed total
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-compute_inventory_total (const struct InventoryTemplateContext *ctx,
- const char *currency,
- struct TALER_Amount *total)
-{
- struct TALER_Amount line_total;
-
- if (NULL == currency)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: currency is NULL\n");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_set_zero (currency,
- total))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: failed to init total for %s\n",
- currency);
- return GNUNET_SYSERR;
- }
- for (unsigned int i = 0; i < ctx->items_len; i++)
- {
- const struct InventoryTemplateItem *item = &ctx->items[i];
- const struct TALER_Amount *unit_price = NULL;
-
- for (size_t j = 0; j < item->pd.price_array_length; j++)
- {
- if (0 == strcmp (item->pd.price_array[j].currency,
- currency))
- {
- unit_price = &item->pd.price_array[j];
- break;
- }
- }
- if (NULL == unit_price)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: missing price in %s for %s\n",
- currency,
- item->product_id);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- compute_line_total (unit_price,
- item->quantity_value,
- item->quantity_frac,
- &line_total))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: line total failed for %s in %s\n",
- item->product_id,
- currency);
- return GNUNET_SYSERR;
- }
- if (0 >
- TALER_amount_add (total,
- total,
- &line_total))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: add failed for %s in %s\n",
- item->product_id,
- currency);
- return GNUNET_SYSERR;
- }
- }
-
- return GNUNET_OK;
-}
-
-
-/**
- * Parse request data for inventory templates.
- *
- * @param connection connection to reply on
- * @param hc handler context
- * @param uc use context
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_using_templates_inventory_request (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
-{
- const json_t *inventory_selection = NULL;
- cleanup_inventory_template_context (&uc->itc);
- uc->summary = NULL;
- uc->inventory.no_amount = false;
- uc->inventory.no_tip = false;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- &uc->summary),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("amount",
- &uc->itc.amount),
- &uc->inventory.no_amount),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("tip",
- &uc->itc.tip),
- &uc->inventory.no_tip),
- GNUNET_JSON_spec_array_const ("inventory_selection",
- &inventory_selection),
- GNUNET_JSON_spec_end ()
- };
- enum GNUNET_GenericReturnValue res;
-
- res = TALER_MHD_parse_json_data (connection,
- hc->request_body,
- spec);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- use_finalize_parse (uc,
- res);
- return GNUNET_SYSERR;
- }
-
- if ( (! uc->inventory.no_amount) ||
- (! uc->inventory.no_tip) )
- {
- if (! TMH_test_exchange_configured_for_currency (
- (! uc->inventory.no_amount)
- ? uc->itc.amount.currency
- : uc->itc.tip.currency))
- {
- GNUNET_break_op (0);
- cleanup_inventory_template_context (&uc->itc);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- "Currency for amount is not supported by backend");
- return GNUNET_SYSERR;
- }
- }
-
- if (! uc->inventory.no_tip)
- {
- if (! uc->inventory.no_amount &&
- (GNUNET_YES !=
- TALER_amount_cmp_currency (&uc->itc.amount,
- &uc->itc.tip)))
- {
- GNUNET_break_op (0);
- cleanup_inventory_template_context (&uc->itc);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- "Mismatch of currencies between the amount and tip");
- return GNUNET_SYSERR;
- }
- }
-
- for (size_t i = 0; i < json_array_size (inventory_selection); i++)
- {
- const char *product_id;
- const char *quantity;
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_string ("product_id",
- &product_id),
- GNUNET_JSON_spec_string ("quantity",
- &quantity),
- GNUNET_JSON_spec_end ()
- };
- const char *err_name;
- unsigned int err_line;
-
- res = GNUNET_JSON_parse (json_array_get (inventory_selection,
- i),
- ispec,
- &err_name,
- &err_line);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- cleanup_inventory_template_context (&uc->itc);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "inventory_selection");
- return GNUNET_SYSERR;
- }
-
- {
- struct InventoryTemplateItem item;
-
- memset (&item,
- 0,
- sizeof (item));
- item.product_id = GNUNET_strdup (product_id);
- item.unit_quantity = GNUNET_strdup (quantity);
- GNUNET_array_append (uc->itc.items,
- uc->itc.items_len,
- item);
- }
- }
- if (0 == uc->itc.items_len)
- {
- GNUNET_break_op (0);
- cleanup_inventory_template_context (&uc->itc);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MISSING,
- "inventory_selection");
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Fetch DB data for inventory templates.
- *
- * @param connection connection to reply on
- * @param hc handler context
- * @param uc use context
+ *
+ * @param[in] items inventory items
+ * @param[in] items_len length of @a items
+ * @param[in] currency currency to total
+ * @param[out] total computed total
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-db_fetch_using_templates_inventory (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
+compute_inventory_total (const struct InventoryTemplateItemContext *items,
+ unsigned int items_len,
+ const char *currency,
+ struct TALER_Amount *total)
{
- struct TMH_MerchantInstance *mi = hc->instance;
+ struct TALER_Amount line_total;
- for (unsigned int i = 0; i < uc->itc.items_len; i++)
+ if (NULL == currency)
{
- struct InventoryTemplateItem *item = &uc->itc.items[i];
- struct TALER_MERCHANTDB_ProductDetails pd;
- enum GNUNET_DB_QueryStatus qs;
- size_t num_categories = 0;
- uint64_t *categories = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_inventory_total: currency is NULL\n");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_set_zero (currency,
+ total))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_inventory_total: failed to init total for %s\n",
+ currency);
+ return GNUNET_SYSERR;
+ }
+ for (unsigned int i = 0; i < items_len; i++)
+ {
+ const struct InventoryTemplateItemContext *item = &items[i];
+ const struct TALER_Amount *unit_price = NULL;
- qs = TMH_db->lookup_product (TMH_db->cls,
- mi->settings.id,
- item->product_id,
- &pd,
- &num_categories,
- &categories);
- if (qs <= 0)
+ for (size_t j = 0; j < item->pd.price_array_length; j++)
{
- enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
- unsigned int http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
-
- switch (qs)
+ if (0 == strcmp (item->pd.price_array[j].currency,
+ currency))
{
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
- break;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
- break;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- http_status = MHD_HTTP_NOT_FOUND;
- ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN;
+ unit_price = &item->pd.price_array[j];
break;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- GNUNET_assert (0);
}
- use_reply_with_error (uc,
- connection,
- http_status,
- ec,
- item->product_id);
+ }
+ if (NULL == unit_price)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_inventory_total: missing price in %s for %s\n",
+ currency,
+ item->product_id);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ compute_line_total (unit_price,
+ item->quantity_value,
+ item->quantity_frac,
+ &line_total))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_inventory_total: line total failed for %s in %s\n",
+ item->product_id,
+ currency);
+ return GNUNET_SYSERR;
+ }
+ if (0 >
+ TALER_amount_add (total,
+ total,
+ &line_total))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_inventory_total: add failed for %s in %s\n",
+ item->product_id,
+ currency);
return GNUNET_SYSERR;
}
-
- item->pd = pd;
- item->categories = categories;
- item->num_categories = num_categories;
}
+
return GNUNET_OK;
}
+/* ***************** USE_PHASE_VERIFY **************** */
+
/**
* Verify request data for inventory templates.
*
@@ -930,27 +1230,27 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
struct GNUNET_JSON_Specification tcspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("summary",
- &uc->inventory.tsummary),
+ &uc->verify.template.inventory.tsummary),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_array_const ("selected_categories",
- &uc->itc.selected_categories),
+ &uc->db_fetch.inventory.selected_categories),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_array_const ("selected_products",
- &uc->itc.selected_products),
+ &uc->db_fetch.inventory.selected_products),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_bool ("choose_one",
- &uc->inventory.choose_one),
+ &uc->verify.template.inventory.choose_one),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_bool ("request_tip",
- &uc->inventory.request_tip),
+ &uc->verify.template.inventory.request_tip),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_bool ("selected_all",
- &uc->itc.selected_all),
+ &uc->db_fetch.inventory.selected_all),
NULL),
GNUNET_JSON_spec_end ()
};
@@ -958,14 +1258,7 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
unsigned int err_line;
enum GNUNET_GenericReturnValue res;
- uc->inventory.tsummary = NULL;
- uc->inventory.choose_one = false;
- uc->inventory.request_tip = false;
- uc->itc.selected_categories = NULL;
- uc->itc.selected_products = NULL;
- uc->itc.selected_all = false;
-
- res = GNUNET_JSON_parse (uc->etp.template_contract,
+ res = GNUNET_JSON_parse (uc->db_fetch.etp.template_contract,
tcspec,
&err_name,
&err_line);
@@ -980,8 +1273,8 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
return GNUNET_SYSERR;
}
- if ( (NULL != uc->summary) &&
- (NULL != uc->inventory.tsummary) )
+ if ( (NULL != uc->parse_request.summary) &&
+ (NULL != uc->verify.template.inventory.tsummary) )
{
GNUNET_break_op (0);
use_reply_with_error (
@@ -992,8 +1285,8 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
NULL);
return GNUNET_SYSERR;
}
- if ( (NULL == uc->summary) &&
- (NULL == uc->inventory.tsummary) )
+ if ( (NULL == uc->parse_request.summary) &&
+ (NULL == uc->verify.template.inventory.tsummary) )
{
GNUNET_break_op (0);
use_reply_with_error (uc,
@@ -1003,10 +1296,11 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
NULL);
return GNUNET_SYSERR;
}
- uc->inventory.no_summary = (NULL == uc->summary);
+ uc->verify.template.inventory.no_summary = (NULL ==
+ uc->parse_request.summary);
- if (! uc->inventory.no_tip &&
- ! uc->inventory.request_tip)
+ if (! uc->parse_request.template.inventory.no_tip &&
+ ! uc->verify.template.inventory.request_tip)
{
GNUNET_break_op (0);
use_reply_with_error (uc,
@@ -1017,8 +1311,8 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
return GNUNET_SYSERR;
}
- if (uc->inventory.choose_one &&
- (1 != uc->itc.items_len))
+ if (uc->verify.template.inventory.choose_one &&
+ (1 != uc->parse_request.template.inventory.items_len))
{
GNUNET_break_op (0);
use_reply_with_error (uc,
@@ -1029,19 +1323,21 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
return GNUNET_SYSERR;
}
- for (unsigned int i = 0; i < uc->itc.items_len; i++)
+ for (unsigned int i = 0; i < uc->parse_request.template.inventory.items_len;
+ i++)
{
- struct InventoryTemplateItem *item = &uc->itc.items[i];
+ struct InventoryTemplateItemContext *item =
+ &uc->parse_request.template.inventory.items[i];
const char *eparam = NULL;
- if (! uc->itc.selected_all)
+ if (! uc->db_fetch.inventory.selected_all)
{
bool allowed = false;
- if (product_id_selected (uc->itc.selected_products,
+ if (product_id_selected (uc->db_fetch.inventory.selected_products,
item->product_id))
allowed = true;
- else if (categories_selected (uc->itc.selected_categories,
+ else if (categories_selected (uc->db_fetch.inventory.selected_categories,
item->num_categories,
item->categories))
allowed = true;
@@ -1090,11 +1386,11 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
}
}
- if (uc->inventory.no_amount &&
- uc->inventory.no_tip)
+ if (uc->parse_request.template.inventory.no_amount &&
+ uc->parse_request.template.inventory.no_tip)
{
if (GNUNET_OK !=
- compute_totals_per_currency (&uc->itc))
+ compute_totals_per_currency (uc))
{
use_reply_with_error (uc,
connection,
@@ -1105,232 +1401,75 @@ verify_using_templates_inventory (struct MHD_Connection *connection,
}
}
- if (! uc->inventory.no_amount ||
- ! uc->inventory.no_tip)
+ if (! uc->parse_request.template.inventory.no_amount ||
+ ! uc->parse_request.template.inventory.no_tip)
{
- const char *primary_currency = uc->inventory.no_amount
- ? uc->itc.tip.currency
- : uc->itc.amount.currency;
-
- GNUNET_array_append (uc->itc.currencies,
- uc->itc.currencies_len,
- (struct InventoryTemplateCurrency) { 0 });
+ const char *primary_currency =
+ uc->parse_request.template.inventory.no_amount
+ ? uc->parse_request.template.inventory.tip.currency
+ : uc->parse_request.template.inventory.amount.currency;
+
+ GNUNET_array_append (uc->verify.totals,
+ uc->verify.totals_len,
+ (struct TALER_Amount) { 0 });
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (primary_currency,
- &uc->itc.currencies[0].total));
+ &uc->verify.totals[0]));
if (GNUNET_OK !=
- compute_inventory_total (&uc->itc,
+ compute_inventory_total (uc->parse_request.template.inventory.items,
+ uc->parse_request.template.inventory.items_len,
primary_currency,
- &uc->itc.currencies[0].total))
+ &uc->verify.totals[0]))
{
use_reply_with_error (uc,
connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "total amount calculation failed");
- return GNUNET_SYSERR;
- }
- }
-
- if (! uc->inventory.no_tip)
- {
- if (GNUNET_YES !=
- TALER_amount_cmp_currency (&uc->itc.tip,
- &uc->itc.currencies[0].total))
- {
- /* YOU NEVER SUPPOSED TO REACH IT */
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- uc->itc.tip.currency);
- return GNUNET_SYSERR;
- }
- if (0 >
- TALER_amount_add (&uc->itc.currencies[0].total,
- &uc->itc.currencies[0].total,
- &uc->itc.tip))
- {
- GNUNET_break (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
- "tip");
- return GNUNET_SYSERR;
- }
- }
-
- if (! uc->inventory.no_amount &&
- (0 != TALER_amount_cmp (&uc->itc.amount,
- &uc->itc.currencies[0].total)))
- {
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
- NULL);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Create order request for inventory templates.
- *
- * @param connection connection to reply on
- * @param uc use context
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-create_using_templates_inventory (struct UseContext *uc)
-{
- json_t *inventory_products;
- json_t *tip_products = NULL;
- const bool multi_choices = uc->inventory.no_amount && uc->inventory.no_tip;
-
- if (NULL != uc->ihc.request_body)
- return GNUNET_OK;
-
- if (! uc->inventory.no_tip)
- {
- tip_products = json_array ();
- GNUNET_assert (0 ==
- json_array_append_new (
- tip_products,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("description",
- "tip"),
- GNUNET_JSON_pack_uint64 ("quantity",
- 1),
- TALER_JSON_pack_amount ("price",
- &uc->itc.tip))));
- }
-
- inventory_products = json_array ();
- GNUNET_assert (NULL != inventory_products);
- for (size_t i = 0; i < uc->itc.items_len; i++)
- {
- const struct InventoryTemplateItem *item = &uc->itc.items[i];
-
- GNUNET_assert (0 ==
- json_array_append_new (
- inventory_products,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("product_id",
- item->product_id),
- GNUNET_JSON_pack_string ("unit_quantity",
- item->unit_quantity))));
- }
-
- {
- json_t *body;
- json_t *choices;
-
- choices = json_array ();
- GNUNET_assert (NULL != choices);
- if (! multi_choices)
- {
- GNUNET_assert (0 ==
- json_array_append_new (choices,
- GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("amount",
- &uc->itc.
- currencies
- [0].total))
- ));
- }
- else
- {
- for (size_t i = 0; i < uc->itc.currencies_len; i++)
- {
- GNUNET_assert (0 ==
- json_array_append_new (
- choices,
- GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("amount",
- &uc->itc.currencies[i].total))));
- }
- }
-
- body = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("otp_id",
- uc->etp.otp_id)),
- GNUNET_JSON_pack_array_steal ("inventory_products",
- inventory_products),
- GNUNET_JSON_pack_object_steal (
- "order",
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("version",
- 1),
- GNUNET_JSON_pack_array_steal ("choices",
- choices),
- GNUNET_JSON_pack_string ("summary",
- uc->inventory.no_summary
- ? uc->inventory.tsummary
- : uc->summary),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("fulfillment_url",
- uc->fulfillment_url)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("fulfillment_message",
- uc->fulfillment_message)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_steal ("products",
- tip_products)))));
- GNUNET_assert (NULL != body);
- uc->ihc.request_body = body;
- }
- cleanup_inventory_template_context (&uc->itc);
- return GNUNET_OK;
-}
-
-
-/**
- * Parse request data for fixed-order templates.
- *
- * @param connection connection to reply on
- * @param hc handler context
- * @param uc use context
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_using_templates_fixed_request (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
-{
- uc->summary = NULL;
- uc->fixed.no_amount = false;
- uc->fixed.no_tip = false;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- &uc->summary),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("amount",
- &uc->fixed.amount),
- &uc->fixed.no_amount),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("tip",
- &uc->fixed.tip),
- &uc->fixed.no_tip),
- GNUNET_JSON_spec_end ()
- };
- enum GNUNET_GenericReturnValue res;
+ "total amount calculation failed");
+ return GNUNET_SYSERR;
+ }
+ }
- res = TALER_MHD_parse_json_data (connection,
- hc->request_body,
- spec);
- if (GNUNET_OK != res)
+ if (! uc->parse_request.template.inventory.no_tip)
+ {
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (&uc->parse_request.template.inventory.tip,
+ &uc->verify.totals[0]))
+ {
+ /* YOU NEVER SUPPOSED TO REACH IT */
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
+ uc->parse_request.template.inventory.tip.currency);
+ return GNUNET_SYSERR;
+ }
+ if (0 >
+ TALER_amount_add (&uc->verify.totals[0],
+ &uc->verify.totals[0],
+ &uc->parse_request.template.inventory.tip))
+ {
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
+ "tip");
+ return GNUNET_SYSERR;
+ }
+ }
+
+ if (! uc->parse_request.template.inventory.no_amount &&
+ (0 != TALER_amount_cmp (&uc->parse_request.template.inventory.amount,
+ &uc->verify.totals[0])))
{
GNUNET_break_op (0);
- use_finalize_parse (uc,
- res);
+ use_reply_with_error (uc,
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
+ NULL);
return GNUNET_SYSERR;
}
return GNUNET_OK;
@@ -1353,16 +1492,16 @@ verify_using_templates_fixed (struct MHD_Connection *connection,
struct GNUNET_JSON_Specification tspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("summary",
- &uc->fixed.tsummary),
+ &uc->verify.template.fixed.tsummary),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("currency",
- &uc->fixed.tcurrency),
+ &uc->verify.template.fixed.tcurrency),
NULL),
GNUNET_JSON_spec_mark_optional (
TALER_JSON_spec_amount_any ("amount",
- &uc->fixed.tamount),
- &uc->fixed.no_tamount),
+ &uc->verify.template.fixed.tamount),
+ &uc->verify.template.fixed.no_tamount),
GNUNET_JSON_spec_uint32 ("minimum_age",
&min_age),
GNUNET_JSON_spec_relative_time ("pay_duration",
@@ -1373,18 +1512,20 @@ verify_using_templates_fixed (struct MHD_Connection *connection,
const char *err_name;
unsigned int err_line;
- uc->fixed.tsummary = NULL;
- uc->fixed.tcurrency = NULL;
- uc->fixed.no_tamount = false;
+ /* USELESS STUFF FROM AI
+ memset (&uc->verify.template.fixed,
+ 0,
+ sizeof (uc->verify.template.fixed));
+ */
- res = GNUNET_JSON_parse (uc->etp.template_contract,
+ res = GNUNET_JSON_parse (uc->db_fetch.etp.template_contract,
tspec,
&err_name,
&err_line);
if (GNUNET_OK != res)
{
GNUNET_break (0);
- json_dumpf (uc->etp.template_contract,
+ json_dumpf (uc->db_fetch.etp.template_contract,
stderr,
JSON_INDENT (2));
use_reply_with_error (uc,
@@ -1395,8 +1536,8 @@ verify_using_templates_fixed (struct MHD_Connection *connection,
return GNUNET_SYSERR;
}
- if ( (! uc->fixed.no_amount) &&
- (! uc->fixed.no_tamount) )
+ if ( (! uc->parse_request.template.fixed.no_amount) &&
+ (! uc->verify.template.fixed.no_tamount) )
{
GNUNET_break_op (0);
use_reply_with_error (uc,
@@ -1407,21 +1548,22 @@ verify_using_templates_fixed (struct MHD_Connection *connection,
return GNUNET_SYSERR;
}
- if ( (! uc->fixed.no_amount) &&
- (NULL != uc->fixed.tcurrency) &&
- (0 != strcmp (uc->fixed.tcurrency,
- uc->fixed.amount.currency)) )
+ if ( (! uc->parse_request.template.fixed.no_amount) &&
+ (NULL != uc->verify.template.fixed.tcurrency) &&
+ (0 != strcmp (uc->verify.template.fixed.tcurrency,
+ uc->parse_request.template.fixed.amount.currency)) )
{
GNUNET_break_op (0);
use_reply_with_error (uc,
connection,
MHD_HTTP_CONFLICT,
TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- uc->fixed.tcurrency);
+ uc->verify.template.fixed.tcurrency);
return GNUNET_SYSERR;
}
- if (uc->fixed.no_amount && uc->fixed.no_tamount)
+ if (uc->parse_request.template.fixed.no_amount &&
+ uc->verify.template.fixed.no_tamount)
{
GNUNET_break_op (0);
use_reply_with_error (uc,
@@ -1432,15 +1574,15 @@ verify_using_templates_fixed (struct MHD_Connection *connection,
return GNUNET_SYSERR;
}
- if (! uc->fixed.no_tip)
+ if (! uc->parse_request.template.fixed.no_tip)
{
const struct TALER_Amount *total_amount;
- total_amount = uc->fixed.no_amount
- ? &uc->fixed.tamount
- : &uc->fixed.amount;
+ total_amount = uc->parse_request.template.fixed.no_amount
+ ? &uc->verify.template.fixed.tamount
+ : &uc->parse_request.template.fixed.amount;
if (GNUNET_YES !=
- TALER_amount_cmp_currency (&uc->fixed.tip,
+ TALER_amount_cmp_currency (&uc->parse_request.template.fixed.tip,
total_amount))
{
GNUNET_break_op (0);
@@ -1448,13 +1590,13 @@ verify_using_templates_fixed (struct MHD_Connection *connection,
connection,
MHD_HTTP_CONFLICT,
TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- uc->fixed.tip.currency);
+ uc->parse_request.template.fixed.tip.currency);
return GNUNET_SYSERR;
}
}
- if ( (NULL != uc->summary) &&
- (NULL != uc->fixed.tsummary) )
+ if ( (NULL != uc->parse_request.summary) &&
+ (NULL != uc->verify.template.fixed.tsummary) )
{
GNUNET_break_op (0);
use_reply_with_error (uc,
@@ -1465,8 +1607,8 @@ verify_using_templates_fixed (struct MHD_Connection *connection,
return GNUNET_SYSERR;
}
- if ( (NULL == uc->summary) &&
- (NULL == uc->fixed.tsummary) )
+ if ( (NULL == uc->parse_request.summary) &&
+ (NULL == uc->verify.template.fixed.tsummary) )
{
GNUNET_break_op (0);
use_reply_with_error (uc,
@@ -1476,30 +1618,88 @@ verify_using_templates_fixed (struct MHD_Connection *connection,
NULL);
return GNUNET_SYSERR;
}
- uc->fixed.no_summary = (NULL == uc->summary);
+ uc->verify.template.fixed.no_summary = (NULL == uc->parse_request.summary);
return GNUNET_OK;
}
/**
- * Create order request for fixed-order templates.
+ * Verify request data for paivana templates.
*
+ * @param connection connection to reply on
* @param uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-create_using_templates_fixed (struct UseContext *uc)
+verify_using_templates_paivana (struct MHD_Connection *connection,
+ struct UseContext *uc)
+{
+ /* TODO: PAIVANA include paivana-specific verification. */
+ return verify_using_templates_fixed (connection,
+ uc);
+}
+
+
+static void
+handle_phase_verify (struct MHD_Connection *connection,
+ struct UseContext *uc)
+{
+ enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
+
+ switch (uc->template_type)
+ {
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ res = verify_using_templates_fixed (connection,
+ uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
+ /* TODO: PAIVANA handle paivana-specific instantiation. */
+ res = verify_using_templates_paivana (connection,
+ uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
+ res = verify_using_templates_inventory (connection,
+ uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
+ GNUNET_break (0);
+ use_reply_with_error (
+ uc,
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "template_contract has unexpected type");
+ break;
+ }
+ if (GNUNET_OK == res)
+ uc->phase = USE_PHASE_CREATE_ORDER;
+}
+
+
+/* ***************** USE_PHASE_CREATE_ORDER **************** */
+
+/**
+ * Create order request for inventory templates.
+ *
+ * @param connection connection to reply on
+ * @param uc use context
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+create_using_templates_inventory (struct UseContext *uc)
{
+ json_t *inventory_products;
json_t *tip_products = NULL;
+ const bool multi_choices = uc->parse_request.template.inventory.no_amount &&
+ uc->parse_request.template.inventory.no_tip;
if (NULL != uc->ihc.request_body)
return GNUNET_OK;
- if (! uc->fixed.no_tip)
+ if (! uc->parse_request.template.inventory.no_tip)
{
tip_products = json_array ();
- GNUNET_assert (NULL != tip_products);
GNUNET_assert (0 ==
json_array_append_new (
tip_products,
@@ -1508,77 +1708,156 @@ create_using_templates_fixed (struct UseContext *uc)
"tip"),
GNUNET_JSON_pack_uint64 ("quantity",
1),
- TALER_JSON_pack_amount ("price",
- &uc->fixed.tip))));
+ TALER_JSON_pack_amount (
+ "price",
+ &uc->parse_request.template.inventory.tip))));
+ }
+
+ inventory_products = json_array ();
+ GNUNET_assert (NULL != inventory_products);
+ for (size_t i = 0; i < uc->parse_request.template.inventory.items_len; i++)
+ {
+ const struct InventoryTemplateItemContext *item =
+ &uc->parse_request.template.inventory.items[i];
+
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ inventory_products,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("product_id",
+ item->product_id),
+ GNUNET_JSON_pack_string ("unit_quantity",
+ item->unit_quantity))));
}
+
{
json_t *body;
+ json_t *choices;
+
+ choices = json_array ();
+ GNUNET_assert (NULL != choices);
+ if (! multi_choices)
+ {
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ choices,
+ GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("amount",
+ &uc->verify.totals[0]))));
+ }
+ else
+ {
+ for (size_t i = 0; i < uc->verify.totals_len; i++)
+ {
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ choices,
+ GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("amount",
+ &uc->verify.totals[i]))));
+ }
+ }
body = GNUNET_JSON_PACK (
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("otp_id",
- uc->etp.otp_id)),
+ uc->db_fetch.etp.otp_id)),
+ GNUNET_JSON_pack_array_steal ("inventory_products",
+ inventory_products),
GNUNET_JSON_pack_object_steal (
"order",
GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("amount",
- uc->fixed.no_amount
- ? &uc->fixed.tamount
- : &uc->fixed.amount),
+ GNUNET_JSON_pack_uint64 ("version",
+ 1),
+ GNUNET_JSON_pack_array_steal ("choices",
+ choices),
GNUNET_JSON_pack_string ("summary",
- uc->fixed.no_summary
- ? uc->fixed.tsummary
- : uc->summary),
+ uc->verify.template.inventory.no_summary
+ ? uc->verify.template.inventory.tsummary
+ : uc->parse_request.summary),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("fulfillment_url",
- uc->fulfillment_url)),
+ uc->parse_request.fulfillment_url)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("fulfillment_message",
- uc->fulfillment_message)),
+ uc->parse_request.fulfillment_message)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_array_steal ("products",
tip_products)))));
+ GNUNET_assert (NULL != body);
uc->ihc.request_body = body;
}
-
+ cleanup_inventory_items (&uc->parse_request.template.inventory.items,
+ &uc->parse_request.template.inventory.items_len);
+ cleanup_inventory_totals (&uc->verify.totals,
+ &uc->verify.totals_len);
return GNUNET_OK;
}
/**
- * Parse request data for paivana templates.
+ * Create order request for fixed-order templates.
*
- * @param connection connection to reply on
- * @param hc handler context
* @param uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-parse_using_templates_paivana_request (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
+create_using_templates_fixed (struct UseContext *uc)
{
- /* TODO: PAIVANA include paivana_id in instantiation flow. */
- return parse_using_templates_fixed_request (connection,
- hc,
- uc);
-}
+ json_t *tip_products = NULL;
+ if (NULL != uc->ihc.request_body)
+ return GNUNET_OK;
-/**
- * Verify request data for paivana templates.
- *
- * @param connection connection to reply on
- * @param uc use context
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-verify_using_templates_paivana (struct MHD_Connection *connection,
- struct UseContext *uc)
-{
- /* TODO: PAIVANA include paivana-specific verification. */
- return verify_using_templates_fixed (connection,
- uc);
+ if (! uc->parse_request.template.fixed.no_tip)
+ {
+ tip_products = json_array ();
+ GNUNET_assert (NULL != tip_products);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ tip_products,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("description",
+ "tip"),
+ GNUNET_JSON_pack_uint64 ("quantity",
+ 1),
+ TALER_JSON_pack_amount (
+ "price",
+ &uc->parse_request.template.fixed.tip))));
+ }
+ {
+ json_t *body;
+
+ body = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_id",
+ uc->db_fetch.etp.otp_id)),
+ GNUNET_JSON_pack_object_steal (
+ "order",
+ GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount (
+ "amount",
+ uc->parse_request.template.fixed.no_amount
+ ? &uc->verify.template.fixed.tamount
+ : &uc->parse_request.template.fixed.amount),
+ GNUNET_JSON_pack_string (
+ "summary",
+ uc->verify.template.fixed.no_summary
+ ? uc->verify.template.fixed.tsummary
+ : uc->parse_request.summary),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("fulfillment_url",
+ uc->parse_request.fulfillment_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("fulfillment_message",
+ uc->parse_request.fulfillment_message)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_steal ("products",
+ tip_products)))));
+ uc->ihc.request_body = body;
+ }
+
+ return GNUNET_OK;
}
@@ -1596,13 +1875,46 @@ create_using_templates_paivana (struct UseContext *uc)
}
+static void
+handle_phase_create_order (struct MHD_Connection *connection,
+ struct UseContext *uc)
+{
+ enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
+
+ switch (uc->template_type)
+ {
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ res = create_using_templates_fixed (uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
+ /* TODO: PAIVANA handle paivana-specific instantiation. */
+ res = create_using_templates_paivana (uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
+ res = create_using_templates_inventory (uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
+ GNUNET_break (0);
+ use_reply_with_error (
+ uc,
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "template_contract has unexpected type");
+ break;
+ }
+ if (GNUNET_OK == res)
+ uc->phase = USE_PHASE_SUBMIT_ORDER;
+}
+
+
+/* ***************** Main handler **************** */
+
MHD_RESULT
TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
- struct TMH_MerchantInstance *mi = hc->instance;
- const char *template_id = hc->infix;
struct UseContext *uc = hc->ctx;
if (NULL == uc)
@@ -1611,208 +1923,37 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh,
hc->ctx = uc;
hc->cc = &cleanup_use_context;
uc->ihc.instance = hc->instance;
- uc->phase = USE_PHASE_LOOKUP_TEMPLATE;
+ uc->phase = USE_PHASE_PARSE_REQUEST;
uc->template_type = TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
- uc->summary = NULL;
- uc->fulfillment_url = NULL;
- uc->fulfillment_message = NULL;
}
while (1)
{
switch (uc->phase)
{
+ case USE_PHASE_PARSE_REQUEST:
+ handle_phase_parse_request (connection,
+ hc,
+ uc);
+ break;
case USE_PHASE_LOOKUP_TEMPLATE:
- if (! uc->have_etp)
- {
- enum GNUNET_DB_QueryStatus qs;
-
- // FIXME: include template type in request, fully parse
- // request before even going into the DB.
- qs = TMH_db->lookup_template (TMH_db->cls,
- mi->settings.id,
- template_id,
- &uc->etp);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- /* Clean up and fail hard */
- GNUNET_break (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- NULL);
- break;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- /* this should be impossible (single select) */
- GNUNET_break (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- NULL);
- break;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* template not found! */
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
- template_id);
- break;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- /* all good */
- uc->have_etp = true;
- break;
- } /* End of the switch */
- if (! uc->have_etp)
- break;
- }
- uc->template_type =
- TMH_template_type_from_contract (uc->etp.template_contract);
- if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == uc->template_type)
- {
- GNUNET_break (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
- break;
- }
- uc->phase = USE_PHASE_PARSE_REQUEST;
+ handle_phase_lookup_template (connection,
+ hc,
+ uc);
break;
- case USE_PHASE_PARSE_REQUEST:
- {
- enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
-
- // FIXME: share more logic between these, just skip (or run) individual
- // phases depending on the template type!
- switch (uc->template_type)
- {
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- res = parse_using_templates_fixed_request (connection,
- hc,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- /* TODO: PAIVANA handle paivana-specific instantiation. */
- res = parse_using_templates_paivana_request (connection,
- hc,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- res = parse_using_templates_inventory_request (connection,
- hc,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- GNUNET_break (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
- break;
- }
- if (GNUNET_OK == res)
- uc->phase = USE_PHASE_DB_FETCH;
- break;
- }
case USE_PHASE_DB_FETCH:
- {
- enum GNUNET_GenericReturnValue res = GNUNET_OK;
-
- switch (uc->template_type)
- {
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- res = db_fetch_using_templates_inventory (connection,
- hc,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- GNUNET_break (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
- break;
- }
- if (GNUNET_OK == res)
- uc->phase = USE_PHASE_VERIFY;
- break;
- }
+ handle_phase_db_fetch (connection,
+ hc,
+ uc);
+ break;
case USE_PHASE_VERIFY:
- {
- enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
-
- switch (uc->template_type)
- {
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- res = verify_using_templates_fixed (connection,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- /* TODO: PAIVANA handle paivana-specific instantiation. */
- res = verify_using_templates_paivana (connection,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- res = verify_using_templates_inventory (connection,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- GNUNET_break (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
- break;
- }
- if (GNUNET_OK == res)
- uc->phase = USE_PHASE_CREATE_ORDER;
- break;
- }
+ handle_phase_verify (connection,
+ uc);
+ break;
case USE_PHASE_CREATE_ORDER:
- {
- enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
-
- switch (uc->template_type)
- {
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- res = create_using_templates_fixed (uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- /* TODO: PAIVANA handle paivana-specific instantiation. */
- res = create_using_templates_paivana (uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- res = create_using_templates_inventory (uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- GNUNET_break (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
- break;
- }
- if (GNUNET_OK == res)
- uc->phase = USE_PHASE_SUBMIT_ORDER;
- break;
- }
+ handle_phase_create_order (connection,
+ uc);
+ break;
case USE_PHASE_SUBMIT_ORDER:
return TMH_private_post_orders (
NULL, /* not even used */