merchant

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

template_parse.c (10426B)


      1 /*
      2   This file is part of TALER
      3   (C) 2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file util/template_parse.c
     18  * @brief shared logic for template contract parsing
     19  * @author Bohdan Potuzhnyi
     20  */
     21 #include "taler/platform.h"
     22 #include <gnunet/gnunet_common.h>
     23 #include <gnunet/gnunet_json_lib.h>
     24 #include <jansson.h>
     25 #include <string.h>
     26 #include <taler/taler_json_lib.h>
     27 #include <taler/taler_util.h>
     28 #include "taler/taler_merchant_util.h"
     29 #include <regex.h>
     30 
     31 
     32 enum TALER_MERCHANT_TemplateType
     33 TALER_MERCHANT_template_type_from_contract (const json_t *template_contract)
     34 {
     35   const json_t *type_val;
     36 
     37   if (NULL == template_contract)
     38     return TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
     39 
     40   type_val = json_object_get (template_contract,
     41                               "template_type");
     42 
     43   if (! json_is_string (type_val))
     44     return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
     45 
     46   return TALER_MERCHANT_template_type_from_string (
     47     json_string_value (type_val));
     48 }
     49 
     50 
     51 enum TALER_MERCHANT_TemplateType
     52 TALER_MERCHANT_template_type_from_string (const char *template_type)
     53 {
     54   if (NULL == template_type)
     55     return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
     56   if (0 == strcmp (template_type,
     57                    "fixed-order"))
     58     return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
     59   if (0 == strcmp (template_type,
     60                    "inventory-cart"))
     61     return TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART;
     62   if (0 == strcmp (template_type,
     63                    "paivana"))
     64     return TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA;
     65   return TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
     66 }
     67 
     68 
     69 const char *
     70 TALER_MERCHANT_template_type_to_string (
     71   enum TALER_MERCHANT_TemplateType template_type)
     72 {
     73   switch (template_type)
     74   {
     75   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
     76     return "fixed-order";
     77   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
     78     return "inventory-cart";
     79   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
     80     return "paivana";
     81   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
     82     break;
     83   }
     84   return NULL;
     85 }
     86 
     87 
     88 /**
     89  * Parse inventory-specific fields from a template contract.
     90  *
     91  * @param template_contract json
     92  * @param[out] out where to write parsed fields
     93  * @param[out] error_name error description
     94  * @return #GNUNET_OK on success, #GNUNET_SYSERR on parse/validation failure
     95  */
     96 static enum GNUNET_GenericReturnValue
     97 parse_template_inventory (const json_t *template_contract,
     98                           struct TALER_MERCHANT_TemplateContract *out,
     99                           const char **error_name)
    100 {
    101   struct GNUNET_JSON_Specification spec[] = {
    102     GNUNET_JSON_spec_mark_optional (
    103       GNUNET_JSON_spec_bool ("selected_all",
    104                              &out->details.inventory.selected_all),
    105       NULL),
    106     GNUNET_JSON_spec_mark_optional (
    107       GNUNET_JSON_spec_array_const ("selected_categories",
    108                                     &out->details.inventory.selected_categories)
    109       ,
    110       NULL),
    111     GNUNET_JSON_spec_mark_optional (
    112       GNUNET_JSON_spec_array_const ("selected_products",
    113                                     &out->details.inventory.selected_products),
    114       NULL),
    115     GNUNET_JSON_spec_mark_optional (
    116       GNUNET_JSON_spec_bool ("choose_one",
    117                              &out->details.inventory.choose_one),
    118       NULL),
    119     GNUNET_JSON_spec_end ()
    120   };
    121   const char *en;
    122 
    123   if (GNUNET_OK !=
    124       GNUNET_JSON_parse ((json_t *) template_contract,
    125                          spec,
    126                          &en,
    127                          NULL))
    128   {
    129     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    130                 "Invalid inventory template_contract for field %s\n",
    131                 en);
    132     if (NULL != error_name)
    133       *error_name = en;
    134     return GNUNET_SYSERR;
    135   }
    136 
    137   if (NULL != out->details.inventory.selected_categories)
    138   {
    139     const json_t *entry;
    140     size_t idx;
    141 
    142     json_array_foreach ((json_t *) out->details.inventory.selected_categories,
    143                         idx,
    144                         entry)
    145     {
    146       if ( (! json_is_integer (entry)) ||
    147            (0 > json_integer_value (entry)) )
    148       {
    149         GNUNET_break_op (0);
    150         if (NULL != error_name)
    151           *error_name = "selected_categories";
    152         return GNUNET_SYSERR;
    153       }
    154     }
    155   }
    156 
    157   if (NULL != out->details.inventory.selected_products)
    158   {
    159     const json_t *entry;
    160     size_t idx;
    161 
    162     json_array_foreach ((json_t *) out->details.inventory.selected_products,
    163                         idx,
    164                         entry)
    165     {
    166       if (! json_is_string (entry))
    167       {
    168         GNUNET_break_op (0);
    169         if (NULL != error_name)
    170           *error_name = "selected_products";
    171         return GNUNET_SYSERR;
    172       }
    173     }
    174   }
    175   return GNUNET_OK;
    176 }
    177 
    178 
    179 /**
    180  * Parse paivana-specific fields from a template contract.
    181  *
    182  * @param template_contract json
    183  * @param[out] out where to write parsed fields
    184  * @param[out] error_name error description
    185  * @return #GNUNET_OK on success, #GNUNET_SYSERR on parse/validation failure
    186  */
    187 static enum GNUNET_GenericReturnValue
    188 parse_template_paivana (const json_t *template_contract,
    189                         struct TALER_MERCHANT_TemplateContract *out,
    190                         const char **error_name)
    191 {
    192   struct GNUNET_JSON_Specification spec[] = {
    193     GNUNET_JSON_spec_mark_optional (
    194       GNUNET_JSON_spec_string ("website_regex",
    195                                &out->details.paivana.website_regex),
    196       NULL),
    197     TALER_MERCHANT_spec_choices ("choices",
    198                                  &out->details.paivana.choices,
    199                                  &out->details.paivana.choices_len),
    200     GNUNET_JSON_spec_end ()
    201   };
    202   const char *en;
    203 
    204   if (GNUNET_OK !=
    205       GNUNET_JSON_parse ((json_t *) template_contract,
    206                          spec,
    207                          &en,
    208                          NULL))
    209   {
    210     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    211                 "Invalid paivana template_contract for field %s\n",
    212                 en);
    213     if (NULL != error_name)
    214       *error_name = en;
    215     return GNUNET_SYSERR;
    216   }
    217   if (NULL != out->details.paivana.website_regex)
    218   {
    219     regex_t ex;
    220 
    221     if (0 != regcomp (&ex,
    222                       out->details.paivana.website_regex,
    223                       REG_NOSUB | REG_EXTENDED))
    224     {
    225       GNUNET_break_op (0);
    226       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    227                   "Invalid paivana website_regex given\n");
    228       if (NULL != error_name)
    229         *error_name = "Invalid website_regex given";
    230       return GNUNET_SYSERR;
    231     }
    232     regfree (&ex);
    233   }
    234   return GNUNET_OK;
    235 }
    236 
    237 
    238 enum GNUNET_GenericReturnValue
    239 TALER_MERCHANT_template_contract_parse (
    240   const json_t *template_contract,
    241   struct TALER_MERCHANT_TemplateContract *out,
    242   const char **error_name)
    243 {
    244   const char *template_type_str = NULL;
    245   struct GNUNET_JSON_Specification spec[] = {
    246     GNUNET_JSON_spec_mark_optional (
    247       GNUNET_JSON_spec_string ("template_type",
    248                                &template_type_str),
    249       NULL),
    250     GNUNET_JSON_spec_mark_optional (
    251       GNUNET_JSON_spec_string ("summary",
    252                                &out->summary),
    253       NULL),
    254     GNUNET_JSON_spec_mark_optional (
    255       GNUNET_JSON_spec_string ("currency",
    256                                &out->currency),
    257       NULL),
    258     GNUNET_JSON_spec_mark_optional (
    259       TALER_JSON_spec_amount_any ("amount",
    260                                   &out->amount),
    261       &out->no_amount),
    262     GNUNET_JSON_spec_mark_optional (
    263       GNUNET_JSON_spec_uint32 ("minimum_age",
    264                                &out->minimum_age),
    265       NULL),
    266     GNUNET_JSON_spec_mark_optional (
    267       GNUNET_JSON_spec_relative_time ("pay_duration",
    268                                       &out->pay_duration),
    269       NULL),
    270     GNUNET_JSON_spec_mark_optional (
    271       GNUNET_JSON_spec_bool ("request_tip",
    272                              &out->request_tip),
    273       NULL),
    274     GNUNET_JSON_spec_end ()
    275   };
    276   const char *en;
    277 
    278   if (NULL == template_contract)
    279   {
    280     if (NULL != error_name)
    281       *error_name = "template_contract is NULL";
    282     return GNUNET_SYSERR;
    283   }
    284 
    285   if (GNUNET_OK !=
    286       GNUNET_JSON_parse ((json_t *) template_contract,
    287                          spec,
    288                          &en,
    289                          NULL))
    290   {
    291     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    292                 "Invalid input for field %s\n",
    293                 en);
    294     if (NULL != error_name)
    295       *error_name = en;
    296     return GNUNET_SYSERR;
    297   }
    298 
    299   out->type = TALER_MERCHANT_template_type_from_string (template_type_str);
    300   if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == out->type)
    301   {
    302     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    303                 "Invalid template_type used '%s'\n",
    304                 template_type_str);
    305     if (NULL != error_name)
    306       *error_name = "Invalid template_type used";
    307     return GNUNET_SYSERR;
    308   }
    309 
    310   /* Parse additional fields for each specific type */
    311   switch (out->type)
    312   {
    313   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
    314     return GNUNET_OK;
    315   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
    316     return parse_template_inventory (template_contract,
    317                                      out,
    318                                      error_name);
    319   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
    320     return parse_template_paivana (template_contract,
    321                                    out,
    322                                    error_name);
    323   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
    324     break;
    325   }
    326 
    327   /* I think we are never supposed to reach it */
    328   GNUNET_break_op (0);
    329   if (NULL != error_name)
    330     *error_name = "template_type";
    331   return GNUNET_SYSERR;
    332 }
    333 
    334 
    335 bool
    336 TALER_MERCHANT_template_contract_valid (const json_t *template_contract)
    337 {
    338   struct TALER_MERCHANT_TemplateContract tmp;
    339 
    340   return (GNUNET_OK ==
    341           TALER_MERCHANT_template_contract_parse (template_contract,
    342                                                   &tmp,
    343                                                   NULL));
    344 }