merchant

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

value_kinds.c (8242B)


      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 value_kinds.c
     18  * @brief Parsing quantities and other decimal fractions
     19  * @author Christian Grothoff
     20  * @author Bohdan
     21  */
     22 #include "taler/platform.h"
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include <gnunet/gnunet_db_lib.h>
     25 #include <taler/taler_json_lib.h>
     26 #include "taler/taler_merchant_util.h"
     27 
     28 
     29 enum GNUNET_GenericReturnValue
     30 TALER_MERCHANT_vk_parse_fractional_string (
     31   const char *value,
     32   int64_t *integer_part,
     33   uint32_t *fractional_part)
     34 {
     35   const char *ptr;
     36   uint64_t integer = 0;
     37   uint32_t frac = 0;
     38   unsigned int digits = 0;
     39 
     40   GNUNET_assert (NULL != integer_part);
     41   GNUNET_assert (NULL != fractional_part);
     42 
     43   if (NULL == value)
     44   {
     45     GNUNET_break_op (0);
     46     return GNUNET_SYSERR;
     47   }
     48   ptr = value;
     49   if ('\0' == *ptr)
     50   {
     51     GNUNET_break_op (0);
     52     return GNUNET_SYSERR;
     53   }
     54   if ('-' == *ptr)
     55   {
     56     GNUNET_break_op (0);
     57     return GNUNET_SYSERR;
     58   }
     59   if (! isdigit ((unsigned char) *ptr))
     60   {
     61     GNUNET_break_op (0);
     62     return GNUNET_SYSERR;
     63   }
     64   while (isdigit ((unsigned char) *ptr))
     65   {
     66     unsigned int digit = (unsigned int) (*ptr - '0');
     67 
     68     if (integer > (UINT64_MAX - digit) / 10)
     69     {
     70       GNUNET_break_op (0);
     71       return GNUNET_SYSERR;
     72     }
     73     integer = integer * 10 + digit;
     74     ptr++;
     75   }
     76   if ('.' == *ptr)
     77   {
     78     ptr++;
     79     if ('\0' == *ptr)
     80     {
     81       GNUNET_break_op (0);
     82       return GNUNET_SYSERR;
     83     }
     84     while (isdigit ((unsigned char) *ptr))
     85     {
     86       unsigned int digit = (unsigned int) (*ptr - '0');
     87 
     88       if (digits >= TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
     89       {
     90         GNUNET_break_op (0);
     91         return GNUNET_SYSERR;
     92       }
     93       frac = (uint32_t) (frac * 10 + digit);
     94       digits++;
     95       ptr++;
     96     }
     97     while (digits < TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
     98     {
     99       frac *= 10;
    100       digits++;
    101     }
    102   }
    103   if ('\0' != *ptr)
    104   {
    105     GNUNET_break_op (0);
    106     return GNUNET_SYSERR;
    107   }
    108   if (integer > (uint64_t) INT64_MAX)
    109   {
    110     GNUNET_break_op (0);
    111     return GNUNET_SYSERR;
    112   }
    113   *integer_part = integer;
    114   *fractional_part = frac;
    115   return GNUNET_OK;
    116 }
    117 
    118 
    119 enum GNUNET_GenericReturnValue
    120 TALER_MERCHANT_vk_process_quantity_inputs (enum TALER_MERCHANT_ValueKind kind,
    121                                            bool allow_fractional,
    122                                            bool int_missing,
    123                                            int64_t int_raw,
    124                                            bool str_missing,
    125                                            const char *str_raw,
    126                                            uint64_t *int_out,
    127                                            uint32_t *frac_out,
    128                                            const char **error_param)
    129 {
    130   static char errbuf[128];
    131   int64_t parsed_int = 0;
    132   uint32_t parsed_frac = 0;
    133   const char *int_field = (TALER_MERCHANT_VK_STOCK == kind)
    134                           ? "total_stock"
    135                           : "quantity";
    136   const char *str_field = (TALER_MERCHANT_VK_STOCK == kind)
    137                           ? "unit_total_stock"
    138                           : "unit_quantity";
    139 
    140   *error_param = NULL;
    141 
    142   if (int_missing && str_missing)
    143   {
    144     GNUNET_snprintf (errbuf,
    145                      sizeof (errbuf),
    146                      "missing %s and %s",
    147                      int_field,
    148                      str_field);
    149     *error_param = errbuf;
    150     GNUNET_break_op (0);
    151     return GNUNET_SYSERR;
    152   }
    153 
    154   if (! str_missing)
    155   {
    156     if ( (TALER_MERCHANT_VK_STOCK == kind) &&
    157          (0 == strcmp ("-1",
    158                        str_raw)) )
    159     {
    160       parsed_int = -1;
    161       parsed_frac = 0;
    162     }
    163     else
    164     {
    165       if (GNUNET_OK !=
    166           TALER_MERCHANT_vk_parse_fractional_string (str_raw,
    167                                                      &parsed_int,
    168                                                      &parsed_frac))
    169       {
    170         GNUNET_snprintf (errbuf,
    171                          sizeof (errbuf),
    172                          "malformed %s",
    173                          str_field);
    174         *error_param = errbuf;
    175         GNUNET_break_op (0);
    176         return GNUNET_SYSERR;
    177       }
    178     }
    179   }
    180 
    181   if ( (! int_missing) && (! str_missing) )
    182   {
    183     if ( (parsed_int != int_raw) || (0 != parsed_frac) )
    184     {
    185       GNUNET_snprintf (errbuf,
    186                        sizeof (errbuf),
    187                        "%s/%s mismatch",
    188                        int_field,
    189                        str_field);
    190       *error_param = errbuf;
    191       GNUNET_break_op (0);
    192       return GNUNET_SYSERR;
    193     }
    194   }
    195   else if (int_missing)
    196   {
    197     int_raw = parsed_int;
    198   }
    199 
    200   if ( (TALER_MERCHANT_VK_STOCK == kind) && (-1 == int_raw) )
    201   {
    202     if ( (! str_missing) && (0 != parsed_frac) )
    203     {
    204       GNUNET_snprintf (errbuf,
    205                        sizeof (errbuf),
    206                        "fractional part forbidden with %s='-1'",
    207                        str_field);
    208       *error_param = errbuf;
    209       GNUNET_break_op (0);
    210       return GNUNET_SYSERR;
    211     }
    212     *int_out = INT64_MAX;
    213     *frac_out = INT32_MAX;
    214     return GNUNET_OK;
    215   }
    216 
    217   if (int_raw < 0)
    218   {
    219     GNUNET_snprintf (errbuf,
    220                      sizeof (errbuf),
    221                      "%s must be non-negative",
    222                      int_field);
    223     *error_param = errbuf;
    224     GNUNET_break_op (0);
    225     return GNUNET_SYSERR;
    226   }
    227 
    228   if (! allow_fractional)
    229   {
    230     if ( (! str_missing) && (0 != parsed_frac) )
    231     {
    232       GNUNET_snprintf (errbuf,
    233                        sizeof (errbuf),
    234                        "fractional part not allowed for %s",
    235                        str_field);
    236       *error_param = errbuf;
    237       GNUNET_break_op (0);
    238       return GNUNET_SYSERR;
    239     }
    240     parsed_frac = 0;
    241   }
    242   else if (! str_missing)
    243   {
    244     if (parsed_frac >= TALER_MERCHANT_UNIT_FRAC_BASE)
    245     {
    246       GNUNET_snprintf (errbuf,
    247                        sizeof (errbuf),
    248                        "%s fractional part exceeds base %u",
    249                        str_field,
    250                        TALER_MERCHANT_UNIT_FRAC_BASE);
    251       *error_param = errbuf;
    252       GNUNET_break_op (0);
    253       return GNUNET_SYSERR;
    254     }
    255   }
    256 
    257   *int_out = (uint64_t) int_raw;
    258   *frac_out = parsed_frac;
    259   return GNUNET_OK;
    260 }
    261 
    262 
    263 void
    264 TALER_MERCHANT_vk_format_fractional_string (
    265   enum TALER_MERCHANT_ValueKind kind,
    266   uint64_t integer,
    267   uint32_t fractional,
    268   size_t buffer_length,
    269   char buffer[static buffer_length])
    270 {
    271   GNUNET_assert (0 < buffer_length);
    272 
    273   if ( (TALER_MERCHANT_VK_STOCK == kind) &&
    274        (INT64_MAX == (int64_t) integer) &&
    275        (INT32_MAX == (int32_t) fractional) )
    276   {
    277     GNUNET_snprintf (buffer,
    278                      buffer_length,
    279                      "-1");
    280     return;
    281   }
    282 
    283   GNUNET_assert ( (TALER_MERCHANT_VK_QUANTITY != kind) ||
    284                   ((INT64_MAX != (int64_t) integer) &&
    285                    (INT32_MAX != (int32_t) fractional)) );
    286   GNUNET_assert (fractional < TALER_MERCHANT_UNIT_FRAC_BASE);
    287 
    288   if (0 == fractional)
    289   {
    290     GNUNET_snprintf (buffer,
    291                      buffer_length,
    292                      "%lu",
    293                      integer);
    294     return;
    295   }
    296   {
    297     char frac_buf[TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS + 1];
    298     size_t idx;
    299 
    300     GNUNET_snprintf (frac_buf,
    301                      sizeof (frac_buf),
    302                      "%0*u",
    303                      TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS,
    304                      (unsigned int) fractional);
    305     for (idx = strlen (frac_buf); idx > 0; idx--)
    306     {
    307       if ('0' != frac_buf[idx - 1])
    308         break;
    309       frac_buf[idx - 1] = '\0';
    310     }
    311     GNUNET_snprintf (buffer,
    312                      buffer_length,
    313                      "%lu.%s",
    314                      integer,
    315                      frac_buf);
    316   }
    317 }