LCOV - code coverage report
Current view: top level - util - order_choice_parse.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 64.6 % 158 102
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2024, 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 src/util/order_choice_parse.c
      18              :  * @brief shared logic for order choice parsing
      19              :  * @author Iván Ávalos
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"
      23              : #include <gnunet/gnunet_common.h>
      24              : #include <gnunet/gnunet_json_lib.h>
      25              : #include <jansson.h>
      26              : #include <stdbool.h>
      27              : #include <stdint.h>
      28              : #include <taler/taler_json_lib.h>
      29              : #include <taler/taler_util.h>
      30              : #include "taler/taler_merchant_util.h"
      31              : 
      32              : 
      33              : /**
      34              :  * Parse JSON order choice input.
      35              :  *
      36              :  * @param[in] root JSON object containing choice input
      37              :  * @param[out] input parsed choice input, NULL if @a input is malformed
      38              :  * @param index index of choice input in inputs array
      39              :  * @return #GNUNET_SYSERR if @a input is malformed; #GNUNET_OK otherwise
      40              :  */
      41              : static enum GNUNET_GenericReturnValue
      42           11 : parse_order_choice_input (
      43              :   json_t *root,
      44              :   struct TALER_MERCHANT_OrderInput *input,
      45              :   size_t index)
      46              : {
      47              :   const char *ename;
      48              :   unsigned int eline;
      49              :   struct GNUNET_JSON_Specification ispec[] = {
      50           11 :     TALER_MERCHANT_json_spec_cit ("type",
      51              :                                   &input->type),
      52           11 :     GNUNET_JSON_spec_end ()
      53              :   };
      54              : 
      55           11 :   if (GNUNET_OK !=
      56           11 :       GNUNET_JSON_parse (root,
      57              :                          ispec,
      58              :                          &ename,
      59              :                          &eline))
      60              :   {
      61            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      62              :                 "Failed to parse %s at %u: %s\n",
      63              :                 ispec[eline].field,
      64              :                 eline,
      65              :                 ename);
      66            0 :     GNUNET_break_op (0);
      67            0 :     return GNUNET_SYSERR;
      68              :   }
      69              : 
      70           11 :   switch (input->type)
      71              :   {
      72            0 :   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
      73            0 :     GNUNET_break (0);
      74            0 :     break;
      75           11 :   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
      76              :     {
      77              :       struct GNUNET_JSON_Specification spec[] = {
      78           11 :         TALER_JSON_spec_slug ("token_family_slug",
      79              :                               &input->details.token.token_family_slug),
      80           11 :         GNUNET_JSON_spec_mark_optional (
      81              :           GNUNET_JSON_spec_uint ("count",
      82              :                                  &input->details.token.count),
      83              :           NULL),
      84           11 :         GNUNET_JSON_spec_end ()
      85              :       };
      86              : 
      87           11 :       if (GNUNET_OK !=
      88           11 :           GNUNET_JSON_parse (root,
      89              :                              spec,
      90              :                              &ename,
      91              :                              &eline))
      92              :       {
      93            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      94              :                     "Failed to parse %s at %u: %s\n",
      95              :                     spec[eline].field,
      96              :                     eline,
      97              :                     ename);
      98            0 :         GNUNET_break_op (0);
      99            0 :         return GNUNET_SYSERR;
     100              :       }
     101              : 
     102           11 :       return GNUNET_OK;
     103              :     }
     104              :   }
     105              : 
     106            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     107              :               "Field 'type' invalid in input #%u\n",
     108              :               (unsigned int) index);
     109            0 :   GNUNET_break_op (0);
     110            0 :   return GNUNET_SYSERR;
     111              : }
     112              : 
     113              : 
     114              : /**
     115              :  * Parse JSON order choice output.
     116              :  *
     117              :  * @param[in] root JSON object containing choice output
     118              :  * @param[out] output parsed choice output, NULL if @a output is malformed
     119              :  * @param index index of choice output in outputs array
     120              :  * @return #GNUNET_SYSERR if @a output is malformed; #GNUNET_OK otherwise
     121              :  */
     122              : static enum GNUNET_GenericReturnValue
     123           10 : parse_order_choice_output (
     124              :   json_t *root,
     125              :   struct TALER_MERCHANT_OrderOutput *output,
     126              :   size_t index)
     127              : {
     128              :   const char *ename;
     129              :   unsigned int eline;
     130              :   struct GNUNET_JSON_Specification ispec[] = {
     131           10 :     TALER_MERCHANT_json_spec_cot ("type",
     132              :                                   &output->type),
     133           10 :     GNUNET_JSON_spec_end ()
     134              :   };
     135              : 
     136           10 :   if (GNUNET_OK !=
     137           10 :       GNUNET_JSON_parse (root,
     138              :                          ispec,
     139              :                          &ename,
     140              :                          &eline))
     141              :   {
     142            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     143              :                 "Failed to parse %s at %u: %s\n",
     144              :                 ispec[eline].field,
     145              :                 eline,
     146              :                 ename);
     147            0 :     GNUNET_break_op (0);
     148            0 :     return GNUNET_SYSERR;
     149              :   }
     150              : 
     151           10 :   switch (output->type)
     152              :   {
     153            0 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     154            0 :     GNUNET_break (0);
     155            0 :     break;
     156            8 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     157              :     {
     158              :       struct GNUNET_JSON_Specification spec[] = {
     159            8 :         TALER_JSON_spec_slug ("token_family_slug",
     160              :                               &output->details.token.token_family_slug),
     161            8 :         GNUNET_JSON_spec_mark_optional (
     162              :           GNUNET_JSON_spec_uint ("count",
     163              :                                  &output->details.token.count),
     164              :           NULL),
     165            8 :         GNUNET_JSON_spec_mark_optional (
     166              :           GNUNET_JSON_spec_timestamp ("valid_at",
     167              :                                       &output->details.token.valid_at),
     168              :           NULL),
     169            8 :         GNUNET_JSON_spec_end ()
     170              :       };
     171              : 
     172            8 :       if (GNUNET_OK !=
     173            8 :           GNUNET_JSON_parse (root,
     174              :                              spec,
     175              :                              &ename,
     176              :                              &eline))
     177              :       {
     178            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     179              :                     "Failed to parse %s at %u: %s\n",
     180              :                     spec[eline].field,
     181              :                     eline,
     182              :                     ename);
     183            0 :         GNUNET_break_op (0);
     184            0 :         return GNUNET_SYSERR;
     185              :       }
     186              : 
     187            8 :       return GNUNET_OK;
     188              :     }
     189            2 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     190              :     {
     191              :       struct GNUNET_JSON_Specification spec[] = {
     192            2 :         GNUNET_JSON_spec_mark_optional (
     193              :           TALER_JSON_spec_amount_any ("amount",
     194              :                                       &output->details.donation_receipt.amount),
     195              :           &output->details.donation_receipt.no_amount),
     196            2 :         GNUNET_JSON_spec_end ()
     197              :       };
     198              : 
     199            2 :       if (GNUNET_OK !=
     200            2 :           GNUNET_JSON_parse (root,
     201              :                              spec,
     202              :                              &ename,
     203              :                              &eline))
     204              :       {
     205            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     206              :                     "Failed to parse %s at %u: %s\n",
     207              :                     spec[eline].field,
     208              :                     eline,
     209              :                     ename);
     210            0 :         GNUNET_break_op (0);
     211            0 :         return GNUNET_SYSERR;
     212              :       }
     213            2 :       return GNUNET_OK;
     214              :     }
     215              :   }
     216              : 
     217            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     218              :               "Field 'type' invalid in output #%u\n",
     219              :               (unsigned int) index);
     220            0 :   GNUNET_break_op (0);
     221            0 :   return GNUNET_SYSERR;
     222              : }
     223              : 
     224              : 
     225              : /**
     226              :  * Parse given JSON object to choices array.
     227              :  *
     228              :  * @param cls closure, pointer to array length
     229              :  * @param root the json array representing the choices
     230              :  * @param[out] ospec where to write the data
     231              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     232              :  */
     233              : static enum GNUNET_GenericReturnValue
     234           37 : parse_order_choices (
     235              :   void *cls,
     236              :   json_t *root,
     237              :   struct GNUNET_JSON_Specification *ospec)
     238              : {
     239           37 :   struct TALER_MERCHANT_OrderChoice **choices = ospec->ptr;
     240           37 :   unsigned int *choices_len = cls;
     241              : 
     242           37 :   if (! json_is_array (root))
     243              :   {
     244            0 :     GNUNET_break_op (0);
     245            0 :     return GNUNET_SYSERR;
     246              :   }
     247           37 :   if (0 == json_array_size (root))
     248              :   {
     249              :     /* empty list of choices is not allowed */
     250            0 :     GNUNET_break_op (0);
     251            0 :     return GNUNET_SYSERR;
     252              :   }
     253           37 :   *choices = NULL;
     254           37 :   *choices_len = 0;
     255           37 :   GNUNET_array_grow (*choices,
     256              :                      *choices_len,
     257              :                      json_array_size (root));
     258              : 
     259           81 :   for (unsigned int i = 0; i < *choices_len; i++)
     260              :   {
     261           44 :     struct TALER_MERCHANT_OrderChoice *choice = &(*choices)[i];
     262           44 :     const json_t *jinputs = NULL;
     263           44 :     const json_t *joutputs = NULL;
     264              :     struct GNUNET_JSON_Specification spec[] = {
     265           44 :       TALER_JSON_spec_amount_any ("amount",
     266              :                                   &choice->amount),
     267           44 :       GNUNET_JSON_spec_mark_optional (
     268              :         TALER_JSON_spec_amount_any ("tip",
     269              :                                     &choice->tip),
     270              :         &choice->no_tip),
     271           44 :       GNUNET_JSON_spec_mark_optional (
     272              :         GNUNET_JSON_spec_string_copy ("description",
     273              :                                       &choice->description),
     274              :         NULL),
     275           44 :       GNUNET_JSON_spec_mark_optional (
     276              :         GNUNET_JSON_spec_object_copy ("description_i18n",
     277              :                                       &choice->description_i18n),
     278              :         NULL),
     279           44 :       GNUNET_JSON_spec_mark_optional (
     280              :         TALER_JSON_spec_amount_any ("max_fee",
     281              :                                     &choice->max_fee),
     282              :         &choice->no_max_fee),
     283           44 :       GNUNET_JSON_spec_mark_optional (
     284              :         GNUNET_JSON_spec_bool ("editable_amount",
     285              :                                &choice->editable_amount),
     286              :         NULL),
     287           44 :       GNUNET_JSON_spec_mark_optional (
     288              :         GNUNET_JSON_spec_array_const ("inputs",
     289              :                                       &jinputs),
     290              :         NULL),
     291           44 :       GNUNET_JSON_spec_mark_optional (
     292              :         GNUNET_JSON_spec_array_const ("outputs",
     293              :                                       &joutputs),
     294              :         NULL),
     295           44 :       GNUNET_JSON_spec_end ()
     296              :     };
     297              :     const char *ename;
     298              :     unsigned int eline;
     299              : 
     300           44 :     if (GNUNET_OK !=
     301           44 :         GNUNET_JSON_parse (json_array_get (root, i),
     302              :                            spec,
     303              :                            &ename,
     304              :                            &eline))
     305              :     {
     306            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     307              :                   "Failed to parse %s at %u: %s\n",
     308              :                   spec[eline].field,
     309              :                   eline,
     310              :                   ename);
     311            0 :       GNUNET_break_op (0);
     312            0 :       return GNUNET_SYSERR;
     313              :     }
     314           48 :     if ( (! choice->no_max_fee) &&
     315              :          (GNUNET_OK !=
     316            4 :           TALER_amount_cmp_currency (&choice->amount,
     317            4 :                                      &choice->max_fee)) )
     318              :     {
     319            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     320              :                   "Fee currency does not match amount currency in choice #%u\n",
     321              :                   i);
     322            0 :       GNUNET_break_op (0);
     323            0 :       return GNUNET_SYSERR;
     324              :     }
     325           48 :     if ( (! choice->no_tip) &&
     326              :          (GNUNET_OK !=
     327            4 :           TALER_amount_cmp_currency (&choice->amount,
     328            4 :                                      &choice->tip)) )
     329              :     {
     330            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     331              :                   "Tip currency does not match amount currency in choice #%u\n",
     332              :                   i);
     333            0 :       GNUNET_break_op (0);
     334            0 :       return GNUNET_SYSERR;
     335              :     }
     336              : 
     337           44 :     if (NULL != jinputs)
     338              :     {
     339              :       const json_t *jinput;
     340              :       size_t idx;
     341              : 
     342           35 :       json_array_foreach ((json_t *) jinputs, idx, jinput)
     343              :       {
     344           11 :         struct TALER_MERCHANT_OrderInput input = {
     345              :           .details.token.count = 1
     346              :         };
     347              : 
     348           11 :         if (GNUNET_OK !=
     349           11 :             parse_order_choice_input ((json_t *) jinput,
     350              :                                       &input,
     351              :                                       idx))
     352              :         {
     353            0 :           GNUNET_break (0);
     354            0 :           return GNUNET_SYSERR;
     355              :         }
     356           11 :         switch (input.type)
     357              :         {
     358            0 :         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
     359            0 :           GNUNET_break_op (0);
     360            0 :           return GNUNET_SYSERR;
     361           11 :         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
     362              :           /* Ignore inputs tokens with 'count' field set to 0 */
     363           11 :           if (0 == input.details.token.count)
     364            4 :             continue;
     365            7 :           break;
     366              :         }
     367            7 :         GNUNET_array_append (choice->inputs,
     368              :                              choice->inputs_len,
     369              :                              input);
     370              :       }
     371              :     }
     372              : 
     373           44 :     if (NULL != joutputs)
     374              :     {
     375              :       const json_t *joutput;
     376              :       size_t idx;
     377           36 :       json_array_foreach ((json_t *) joutputs, idx, joutput)
     378              :       {
     379           10 :         struct TALER_MERCHANT_OrderOutput output = {
     380              :           .details.token.count = 1
     381              :         };
     382              : 
     383           10 :         if (GNUNET_OK !=
     384           10 :             parse_order_choice_output ((json_t *) joutput,
     385              :                                        &output,
     386              :                                        idx))
     387              :         {
     388            0 :           GNUNET_break (0);
     389            0 :           return GNUNET_SYSERR;
     390              :         }
     391           10 :         switch (output.type)
     392              :         {
     393            0 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     394            0 :           GNUNET_break_op (0);
     395            0 :           return GNUNET_SYSERR;
     396            8 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     397              :           /* Ignore output tokens with 'count' field set to 0 */
     398            8 :           if (0 == output.details.token.count)
     399            0 :             continue;
     400            8 :           break;
     401            2 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     402            2 :           break;
     403              :         }
     404           10 :         GNUNET_array_append (choice->outputs,
     405              :                              choice->outputs_len,
     406              :                              output);
     407              :       }
     408              :     }
     409              :   }
     410              : 
     411           37 :   return GNUNET_OK;
     412              : }
     413              : 
     414              : 
     415              : struct GNUNET_JSON_Specification
     416           37 : TALER_MERCHANT_spec_order_choices (
     417              :   const char *name,
     418              :   struct TALER_MERCHANT_OrderChoice **choices,
     419              :   unsigned int *choices_len)
     420              : {
     421           37 :   struct GNUNET_JSON_Specification ret = {
     422              :     .cls = (void *) choices_len,
     423              :     .parser = &parse_order_choices,
     424              :     .field = name,
     425              :     .ptr = choices,
     426              :   };
     427              : 
     428           37 :   return ret;
     429              : }
     430              : 
     431              : 
     432              : void
     433           44 : TALER_MERCHANT_order_choice_free (
     434              :   struct TALER_MERCHANT_OrderChoice *choice)
     435              : {
     436           54 :   for (unsigned int i = 0; i < choice->outputs_len; i++)
     437              :   {
     438           10 :     struct TALER_MERCHANT_OrderOutput *output = &choice->outputs[i];
     439              : 
     440           10 :     switch (output->type)
     441              :     {
     442            0 :     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     443            0 :       GNUNET_break (0);
     444            0 :       break;
     445            8 :     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     446            8 :       break;
     447            2 :     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     448            2 :       break;
     449              : #if FUTURE
     450              :     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN:
     451              :       GNUNET_free (output->details.coin.exchange_url);
     452              :       break;
     453              : #endif
     454              :     }
     455              :   }
     456           44 :   GNUNET_free (choice->description);
     457           44 :   if (NULL != choice->description_i18n)
     458              :   {
     459            0 :     json_decref (choice->description_i18n);
     460            0 :     choice->description_i18n = NULL;
     461              :   }
     462           44 :   GNUNET_free (choice->inputs);
     463           44 :   GNUNET_free (choice->outputs);
     464           44 : }
        

Generated by: LCOV version 2.0-1