gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] 11/31: implement parsing of choices array for v1 contra


From: gnunet
Subject: [taler-merchant] 11/31: implement parsing of choices array for v1 contracts
Date: Thu, 18 Apr 2024 08:39:04 +0200

This is an automated email from the git hooks/post-receive script.

christian-blaettler pushed a commit to branch master
in repository merchant.

commit a290b4b02940bd702720b469a8c88a6ee931813b
Author: Christian Blättler <blatc2@bfh.ch>
AuthorDate: Sun Mar 10 14:07:24 2024 +0100

    implement parsing of choices array for v1 contracts
---
 .../taler-merchant-httpd_private-post-orders.c     | 394 +++++++++++++--------
 1 file changed, 241 insertions(+), 153 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c 
b/src/backend/taler-merchant-httpd_private-post-orders.c
index bea9fd51..1c25da4e 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -228,31 +228,14 @@ struct OrderContext
   struct
   {
     /**
-     * Our order ID.
-     */
-    const char *order_id;
-
-    /**
-     * Array of possible specific contracts the wallet/customer may choose
-     * from by selecting the respective index when signing the deposit
-     * confirmation.
-     */
-    struct TALER_MerchantContractChoice *choices;
-
-    /**
-     * Length of the @e choices array.
-     */
-    unsigned int choices_len;
-
-    /**
-     * Summary of the order.
+     * Version of the contract terms.
      */
-    // const char *summary;
+    enum TALER_MerchantContractVersion version;
 
     /**
-     * Internationalized summary.
+     * Our order ID.
      */
-    // json_t *summary_i18n;
+    const char *order_id;
 
     /**
      * URL where the same contract could be ordered again (if available).
@@ -260,21 +243,9 @@ struct OrderContext
     const char *public_reorder_url;
 
     /**
-     * URL that will show that the order was successful
-     * after it has been paid for.
-     */
-    // const char *fulfillment_url;
-
-    /**
-     * Message shown to the customer after paying for the order.
-     * Either fulfillment_url or fulfillment_message must be specified.
-     */
-    // const char *fulfillment_message;
-
-    /**
-     * Map from IETF BCP 47 language tags to localized fulfillment messages.
-     */
-    // json_t *fulfillment_message_i18n;
+    * Optional array of contract choices. Is null for v0 contracts.
+    */
+    const json_t *choices;
 
     /**
      * Merchant base URL.
@@ -311,38 +282,12 @@ struct OrderContext
      */
     json_t *delivery_location;
 
-    /**
-     * Array of products that are part of the purchase.
-     */
-    // const json_t *products;
-
-    /**
-     * TODO: Maybe remove this and set it from settings where we serialize
-     *       the order to JSON?
-     *
-     * Information like name, website, email, etc. about the merchant.
-     */
-    // json_t *merchant;
-
-    /**
-     * TODO: Maybe remove this and set it from settings where we serialize
-     *       the order to JSON?
-     *
-     * Merchant's public key
-     */
-    // struct TALER_MerchantPublicKeyP merchant_pub;
-
     /**
     * Gross amount value of the contract. Used to
     * compute @e max_stefan_fee.
     */
     struct TALER_Amount brutto;
 
-    /**
-     * Maximum fee as given by the client request.
-     */
-    // struct TALER_Amount max_fee;
-
     /**
      * Array of fee limits and wire account details by currency.
      */
@@ -372,6 +317,24 @@ struct OrderContext
 
   } parse_order;
 
+  /**
+   * Information set in the ORDER_PHASE_PARSE_CHOICES phase.
+   */
+  struct
+  {
+    /**
+     * Array of possible specific contracts the wallet/customer may choose
+     * from by selecting the respective index when signing the deposit
+     * confirmation.
+     */
+    struct TALER_MerchantContractChoice *choices;
+
+    /**
+     * Length of the @e choices array.
+     */
+    unsigned int choices_len;
+  } parse_choices;
+
   /**
    * Information set in the ORDER_PHASE_MERGE_INVENTORY phase.
    */
@@ -432,11 +395,6 @@ struct OrderContext
    */
   struct
   {
-    /**
-     * Maximum fee
-     */
-    // struct TALER_Amount max_fee;
-
     /**
      * Array of fee limits and wire account details by currency.
      */
@@ -500,6 +458,7 @@ struct OrderContext
   {
     ORDER_PHASE_PARSE_REQUEST,
     ORDER_PHASE_PARSE_ORDER,
+    ORDER_PHASE_PARSE_CHOICES,
     ORDER_PHASE_MERGE_INVENTORY,
     ORDER_PHASE_ADD_PAYMENT_DETAILS,
     ORDER_PHASE_SET_EXCHANGES,
@@ -638,17 +597,17 @@ clean_order (void *cls)
     oc->set_exchanges.exchanges = NULL;
   }
   /* TODO: Clean choices array */
-  for (unsigned int i = 0; i<oc->parse_order.choices_len; i++)
+  for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
   {
-    if (NULL != oc->parse_order.choices[i].fulfillment_message_i18n)
+    if (NULL != oc->parse_choices.choices[i].fulfillment_message_i18n)
     {
-      json_decref (oc->parse_order.choices[i].fulfillment_message_i18n);
-      oc->parse_order.choices[i].fulfillment_message_i18n = NULL;
+      json_decref (oc->parse_choices.choices[i].fulfillment_message_i18n);
+      oc->parse_choices.choices[i].fulfillment_message_i18n = NULL;
     }
-    if (NULL != oc->parse_order.choices[i].summary_i18n)
+    if (NULL != oc->parse_choices.choices[i].summary_i18n)
     {
-      json_decref (oc->parse_order.choices[i].summary_i18n);
-      oc->parse_order.choices[i].summary_i18n = NULL;
+      json_decref (oc->parse_choices.choices[i].summary_i18n);
+      oc->parse_choices.choices[i].summary_i18n = NULL;
     }
     if (NULL != oc->parse_order.delivery_location)
     {
@@ -1351,7 +1310,7 @@ serialize_order (struct OrderContext *oc)
   }
 
   // TODO: How to properly handle these cases / errors?
-  GNUNET_assert (1 == oc->parse_order.choices_len && NULL != choice);
+  GNUNET_assert (1 == oc->parse_choices.choices_len && NULL != choice);
   GNUNET_assert (1 == oc->set_max_fee.limits_len && NULL != limits);
 
   oc->serialize_order.contract = GNUNET_JSON_PACK (
@@ -1541,40 +1500,33 @@ set_exchanges (struct OrderContext *oc)
 
 
 /**
- * Add missing fields to the order. Upon success, continue
- * processing with merge_inventory().
+ * Parse the order field of the request. Upon success, continue
+ * processing with parse_choices().
  *
  * @param[in,out] oc order context
  */
 static void
 parse_order (struct OrderContext *oc)
 {
-  struct TALER_MerchantContractChoice *choice;
   struct TALER_MerchantContractLimits *limits;
 
   const struct TALER_MERCHANTDB_InstanceSettings *settings =
     &oc->hc->instance->settings;
   const char *merchant_base_url = NULL;
+  const char *version = NULL;
   const json_t *jmerchant = NULL;
-  choice = GNUNET_new (struct TALER_MerchantContractChoice);
   limits = GNUNET_new (struct TALER_MerchantContractLimits);
   /* auto_refund only needs to be type-checked,
    * mostly because in GNUnet relative times can't
    * be negative.  */
   bool no_fee;
   struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount_any ("amount",
-                                &oc->parse_order.brutto),
-    GNUNET_JSON_spec_string ("summary",
-                             &choice->summary),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_array_const ("products",
-                                    &choice->products),
-      NULL),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_json ("summary_i18n",
-                             &choice->summary_i18n),
+      GNUNET_JSON_spec_string ("version",
+                               &version),
       NULL),
+    TALER_JSON_spec_amount_any ("amount",
+                                &oc->parse_order.brutto),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_string ("order_id",
                                &oc->parse_order.order_id),
@@ -1584,16 +1536,8 @@ parse_order (struct OrderContext *oc)
                                &oc->parse_order.public_reorder_url),
       NULL),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("fulfillment_message",
-                               &choice->fulfillment_message),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_json ("fulfillment_message_i18n",
-                             &choice->fulfillment_message_i18n),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("fulfillment_url",
-                               &choice->fulfillment_url),
+      GNUNET_JSON_spec_array_const ("choices",
+                                   &oc->parse_order.choices),
       NULL),
     GNUNET_JSON_spec_mark_optional (
       TALER_JSON_spec_web_url ("merchant_base_url",
@@ -1655,6 +1599,34 @@ parse_order (struct OrderContext *oc)
                      ret);
     return;
   }
+  if (NULL == version || 0 == strcmp("v0", version))
+  {
+    oc->parse_order.version = TALER_MCV_V0;
+  }
+  else if (0 != strcmp("v1", version))
+  {
+    oc->parse_order.version = TALER_MCV_V1;
+  }
+  else
+  {
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (spec);
+    reply_with_error (oc,
+                      MHD_HTTP_BAD_REQUEST,
+                      TALER_EC_GENERIC_VERSION_MALFORMED,
+                      "invalid version specified in order, supported are null, 
'v0' or 'v1'");
+    return;
+  }
+  if (NULL == oc->parse_order.choices && TALER_MCV_V0 != 
oc->parse_order.version)
+  {
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (spec);
+    reply_with_error (oc,
+                      MHD_HTTP_BAD_REQUEST,
+                      TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR,
+                      "choices array must not be null for v1 contracts");
+    return;
+  }
   if (! TMH_test_exchange_configured_for_currency (
         oc->parse_order.brutto.currency))
   {
@@ -1732,46 +1704,6 @@ parse_order (struct OrderContext *oc)
     GNUNET_assert (NULL != oc->parse_order.order_id);
   }
 
-  /* Patch fulfillment URL with order_id (implements #6467). */
-  if (NULL != choice->fulfillment_url)
-  {
-    const char *pos;
-
-    pos = strstr (choice->fulfillment_url,
-                  "${ORDER_ID}");
-    if (NULL != pos)
-    {
-      /* replace ${ORDER_ID} with the real order_id */
-      char *nurl;
-
-      /* We only allow one placeholder */
-      if (strstr (pos + strlen ("${ORDER_ID}"),
-                  "${ORDER_ID}"))
-      {
-        GNUNET_break_op (0);
-        reply_with_error (oc,
-                          MHD_HTTP_BAD_REQUEST,
-                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                          "fulfillment_url");
-        return;
-      }
-
-      GNUNET_asprintf (&nurl,
-                       "%.*s%s%s",
-                       /* first output URL until ${ORDER_ID} */
-                       (int) (pos - choice->fulfillment_url),
-                       choice->fulfillment_url,
-                       /* replace ${ORDER_ID} with the right order_id */
-                       oc->parse_order.order_id,
-                       /* append rest of original URL */
-                       pos + strlen ("${ORDER_ID}"));
-
-      choice->fulfillment_url = GNUNET_strdup (nurl);
-
-      GNUNET_free (nurl);
-    }
-  }
-
   /* Check soundness of refund deadline, and that a timestamp
    * is actually present.  */
   {
@@ -1914,18 +1846,6 @@ parse_order (struct OrderContext *oc)
     oc->parse_order.merchant_base_url = url;
   }
 
-  if ( (NULL != choice->products) &&
-       (! TMH_products_array_valid (choice->products)) )
-  {
-    GNUNET_break_op (0);
-    reply_with_error (
-      oc,
-      MHD_HTTP_BAD_REQUEST,
-      TALER_EC_GENERIC_PARAMETER_MALFORMED,
-      "order.products");
-    return;
-  }
-
   /* Merchant information must not already be present */
   if (NULL != jmerchant)
   {
@@ -1952,6 +1872,171 @@ parse_order (struct OrderContext *oc)
   oc->phase++;
 }
 
+/**
+ * Parse a json contract choice.
+ *
+ * @param[out] choice resulting contract choice
+ * @param root json object
+ * @param order_id order_id to be replaced in fulfillment URL
+ * @return #GNUNET_OK if choice could be parsed.
+ */
+static enum GNUNET_GenericReturnValue
+parse_choice (
+  struct TALER_MerchantContractChoice *choice,
+  json_t *root,
+  const char *order_id)
+{
+  const char *error_name;
+  unsigned int error_line;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("summary",
+                            &choice->summary),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_array_const ("products",
+                                    &choice->products),
+      NULL),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_json ("summary_i18n",
+                            &choice->summary_i18n),
+      NULL),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_string ("fulfillment_message",
+                              &choice->fulfillment_message),
+      NULL),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_json ("fulfillment_message_i18n",
+                            &choice->fulfillment_message_i18n),
+      NULL),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_string ("fulfillment_url",
+                              &choice->fulfillment_url),
+      NULL),
+  };
+
+  enum GNUNET_GenericReturnValue ret;
+  ret = GNUNET_JSON_parse (root,
+                           spec,
+                           &error_name,
+                           &error_line);
+  if (GNUNET_OK != ret)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Choice parsing failed: %s:%u\n",
+                error_name,
+                error_line);
+    return ret;
+  }
+
+  /* Patch fulfillment URL with order_id (implements #6467). */
+  if (NULL != choice->fulfillment_url)
+  {
+    const char *pos;
+
+    pos = strstr (choice->fulfillment_url,
+                  "${ORDER_ID}");
+    if (NULL != pos)
+    {
+      /* replace ${ORDER_ID} with the real order_id */
+      char *nurl;
+
+      /* We only allow one placeholder */
+      if (strstr (pos + strlen ("${ORDER_ID}"),
+                  "${ORDER_ID}"))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Patching fulfillment URL failed\n");
+        return GNUNET_SYSERR;
+      }
+
+      GNUNET_asprintf (&nurl,
+                      "%.*s%s%s",
+                      /* first output URL until ${ORDER_ID} */
+                      (int) (pos - choice->fulfillment_url),
+                      choice->fulfillment_url,
+                      /* replace ${ORDER_ID} with the right order_id */
+                      order_id,
+                      /* append rest of original URL */
+                      pos + strlen ("${ORDER_ID}"));
+
+      choice->fulfillment_url = GNUNET_strdup (nurl);
+
+      GNUNET_free (nurl);
+    }
+  }
+
+  if ( (NULL != choice->products) &&
+      (! TMH_products_array_valid (choice->products)) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Invalid choice.products array\n");
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+/**
+ * Check contract version and call parse_choices_v0 or parse_choices_v1
+ * respectively. Upon success, continue processing with merge_inventory().
+ *
+ * @param[in,out] oc order context
+ */
+static void
+parse_choices (struct OrderContext *oc)
+{
+  enum GNUNET_GenericReturnValue ret;
+
+  if (TALER_MCV_V0 == oc->parse_order.version)
+  {
+    /**
+    * v0 contract is basically a v1 contract with only one choice
+    * and no inputs or outputs.
+    */
+    GNUNET_array_grow (oc->parse_choices.choices,
+                       oc->parse_choices.choices_len,
+                       1);
+
+    ret = parse_choice(oc->parse_choices.choices,
+                            oc->parse_request.order,
+                            oc->parse_order.order_id);
+
+    if (GNUNET_OK != ret)
+    {
+      GNUNET_break_op (0);
+      reply_with_error (oc,
+                        MHD_HTTP_BAD_REQUEST,
+                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                        "order");
+      return;
+    }
+  }
+  else if (TALER_MCV_V1 == oc->parse_order.version)
+  {
+    GNUNET_assert (NULL != oc->parse_order.choices);
+
+    GNUNET_array_grow (oc->parse_choices.choices,
+                       oc->parse_choices.choices_len,
+                       json_array_size (oc->parse_order.choices));
+    for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
+    {
+      ret = parse_choice(&oc->parse_choices.choices[i],
+                              json_array_get (oc->parse_order.choices, i),
+                              oc->parse_order.order_id);
+
+      if (GNUNET_OK != ret)
+      {
+        GNUNET_break_op (0);
+        reply_with_error (oc,
+                          MHD_HTTP_BAD_REQUEST,
+                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                          "choices");
+        return;
+      }
+    }
+  }
+
+  oc->phase++;
+}
 
 /**
  * Process the @a payment_target and add the details of how the
@@ -1999,9 +2084,9 @@ add_payment_details (struct OrderContext *oc)
 static void
 merge_inventory (struct OrderContext *oc)
 {
-  struct TALER_MerchantContractChoice *choice = oc->parse_order.choices;
+  struct TALER_MerchantContractChoice *choice = oc->parse_choices.choices;
 
-  GNUNET_assert (1 == oc->parse_order.choices_len && NULL != choice);
+  GNUNET_assert (1 == oc->parse_choices.choices_len && NULL != choice);
 
   /**
    * parse_request.inventory_products => instructions to add products to 
contract terms
@@ -2338,6 +2423,9 @@ TMH_private_post_orders (
     case ORDER_PHASE_PARSE_ORDER:
       parse_order (oc);
       break;
+    case ORDER_PHASE_PARSE_CHOICES:
+      parse_choices (oc);
+      break;
     case ORDER_PHASE_MERGE_INVENTORY:
       merge_inventory (oc);
       break;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]