LCOV - code coverage report
Current view: top level - util - util.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 77.0 % 217 167
Test Date: 2026-09-11 18:55:36 Functions: 77.8 % 18 14

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2014-2023 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU 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.c
      18              :  * @brief Common utility functions
      19              :  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
      20              :  * @author Florian Dold
      21              :  * @author Benedikt Mueller
      22              :  * @author Christian Grothoff
      23              :  */
      24              : #include "platform.h"
      25              : #include "taler/taler_util.h"
      26              : #include <gnunet/gnunet_json_lib.h>
      27              : #include <unistr.h>
      28              : 
      29              : 
      30              : int
      31            0 : TALER_strcmp_ct (const char *a,
      32              :                  const char *b)
      33              : {
      34            0 :   size_t alen = strlen (a);
      35              : 
      36            0 :   if (strlen (b) != alen)
      37            0 :     return 1;
      38            0 :   return GNUNET_memcmp_ct_ (a,
      39              :                             b,
      40              :                             alen);
      41              : }
      42              : 
      43              : 
      44              : const char *
      45         2224 : TALER_b2s (const void *buf,
      46              :            size_t buf_size)
      47              : {
      48              :   static TALER_THREAD_LOCAL char ret[9];
      49              :   struct GNUNET_HashCode hc;
      50              :   char *tmp;
      51              : 
      52         2224 :   GNUNET_CRYPTO_hash (buf,
      53              :                       buf_size,
      54              :                       &hc);
      55         2224 :   tmp = GNUNET_STRINGS_data_to_string_alloc (&hc,
      56              :                                              sizeof (hc));
      57         2224 :   GNUNET_memcpy (ret,
      58              :                  tmp,
      59              :                  8);
      60         2224 :   GNUNET_free (tmp);
      61         2224 :   ret[8] = '\0';
      62         2224 :   return ret;
      63              : }
      64              : 
      65              : 
      66              : void
      67         3816 : TALER_denom_fee_set_hton (struct TALER_DenomFeeSetNBOP *nbo,
      68              :                           const struct TALER_DenomFeeSet *fees)
      69              : {
      70         3816 :   TALER_amount_hton (&nbo->withdraw,
      71              :                      &fees->withdraw);
      72         3816 :   TALER_amount_hton (&nbo->deposit,
      73              :                      &fees->deposit);
      74         3816 :   TALER_amount_hton (&nbo->refresh,
      75              :                      &fees->refresh);
      76         3816 :   TALER_amount_hton (&nbo->refund,
      77              :                      &fees->refund);
      78         3816 : }
      79              : 
      80              : 
      81              : void
      82            0 : TALER_denom_fee_set_ntoh (struct TALER_DenomFeeSet *fees,
      83              :                           const struct TALER_DenomFeeSetNBOP *nbo)
      84              : {
      85            0 :   TALER_amount_ntoh (&fees->withdraw,
      86              :                      &nbo->withdraw);
      87            0 :   TALER_amount_ntoh (&fees->deposit,
      88              :                      &nbo->deposit);
      89            0 :   TALER_amount_ntoh (&fees->refresh,
      90              :                      &nbo->refresh);
      91            0 :   TALER_amount_ntoh (&fees->refund,
      92              :                      &nbo->refund);
      93            0 : }
      94              : 
      95              : 
      96              : void
      97          182 : TALER_global_fee_set_hton (struct TALER_GlobalFeeSetNBOP *nbo,
      98              :                            const struct TALER_GlobalFeeSet *fees)
      99              : {
     100          182 :   TALER_amount_hton (&nbo->history,
     101              :                      &fees->history);
     102          182 :   TALER_amount_hton (&nbo->account,
     103              :                      &fees->account);
     104          182 :   TALER_amount_hton (&nbo->purse,
     105              :                      &fees->purse);
     106          182 : }
     107              : 
     108              : 
     109              : void
     110            0 : TALER_global_fee_set_ntoh (struct TALER_GlobalFeeSet *fees,
     111              :                            const struct TALER_GlobalFeeSetNBOP *nbo)
     112              : {
     113            0 :   TALER_amount_ntoh (&fees->history,
     114              :                      &nbo->history);
     115            0 :   TALER_amount_ntoh (&fees->account,
     116              :                      &nbo->account);
     117            0 :   TALER_amount_ntoh (&fees->purse,
     118              :                      &nbo->purse);
     119            0 : }
     120              : 
     121              : 
     122              : void
     123          208 : TALER_wire_fee_set_hton (struct TALER_WireFeeSetNBOP *nbo,
     124              :                          const struct TALER_WireFeeSet *fees)
     125              : {
     126          208 :   TALER_amount_hton (&nbo->wire,
     127              :                      &fees->wire);
     128          208 :   TALER_amount_hton (&nbo->closing,
     129              :                      &fees->closing);
     130          208 : }
     131              : 
     132              : 
     133              : void
     134            0 : TALER_wire_fee_set_ntoh (struct TALER_WireFeeSet *fees,
     135              :                          const struct TALER_WireFeeSetNBOP *nbo)
     136              : {
     137            0 :   TALER_amount_ntoh (&fees->wire,
     138              :                      &nbo->wire);
     139            0 :   TALER_amount_ntoh (&fees->closing,
     140              :                      &nbo->closing);
     141            0 : }
     142              : 
     143              : 
     144              : int
     145            3 : TALER_global_fee_set_cmp (const struct TALER_GlobalFeeSet *f1,
     146              :                           const struct TALER_GlobalFeeSet *f2)
     147              : {
     148              :   int ret;
     149              : 
     150            3 :   ret = TALER_amount_cmp (&f1->history,
     151              :                           &f2->history);
     152            3 :   if (0 != ret)
     153            1 :     return ret;
     154            2 :   ret = TALER_amount_cmp (&f1->account,
     155              :                           &f2->account);
     156            2 :   if (0 != ret)
     157            0 :     return ret;
     158            2 :   ret = TALER_amount_cmp (&f1->purse,
     159              :                           &f2->purse);
     160            2 :   if (0 != ret)
     161            0 :     return ret;
     162            2 :   return 0;
     163              : }
     164              : 
     165              : 
     166              : int
     167            7 : TALER_wire_fee_set_cmp (const struct TALER_WireFeeSet *f1,
     168              :                         const struct TALER_WireFeeSet *f2)
     169              : {
     170              :   int ret;
     171              : 
     172            7 :   ret = TALER_amount_cmp (&f1->wire,
     173              :                           &f2->wire);
     174            7 :   if (0 != ret)
     175            1 :     return ret;
     176            6 :   ret = TALER_amount_cmp (&f1->closing,
     177              :                           &f2->closing);
     178            6 :   if (0 != ret)
     179            2 :     return ret;
     180            4 :   return 0;
     181              : }
     182              : 
     183              : 
     184              : enum GNUNET_GenericReturnValue
     185         1114 : TALER_denom_fee_check_currency (
     186              :   const char *currency,
     187              :   const struct TALER_DenomFeeSet *fees)
     188              : {
     189         1114 :   if (GNUNET_YES !=
     190         1114 :       TALER_amount_is_currency (&fees->withdraw,
     191              :                                 currency))
     192              :   {
     193            0 :     GNUNET_break (0);
     194            0 :     return GNUNET_NO;
     195              :   }
     196         1114 :   if (GNUNET_YES !=
     197         1114 :       TALER_amount_is_currency (&fees->deposit,
     198              :                                 currency))
     199              :   {
     200            0 :     GNUNET_break (0);
     201            0 :     return GNUNET_NO;
     202              :   }
     203         1114 :   if (GNUNET_YES !=
     204         1114 :       TALER_amount_is_currency (&fees->refresh,
     205              :                                 currency))
     206              :   {
     207            0 :     GNUNET_break (0);
     208            0 :     return GNUNET_NO;
     209              :   }
     210         1114 :   if (GNUNET_YES !=
     211         1114 :       TALER_amount_is_currency (&fees->refund,
     212              :                                 currency))
     213              :   {
     214            0 :     GNUNET_break (0);
     215            0 :     return GNUNET_NO;
     216              :   }
     217         1114 :   return GNUNET_OK;
     218              : }
     219              : 
     220              : 
     221              : /**
     222              :  * Dump character in the low range into @a buf
     223              :  * following RFC 8785.
     224              :  *
     225              :  * @param[in,out] buf buffer to modify
     226              :  * @param val value to dump
     227              :  */
     228              : static void
     229            3 : lowdump (struct GNUNET_Buffer *buf,
     230              :          unsigned char val)
     231              : {
     232              :   char scratch[7];
     233              : 
     234            3 :   switch (val)
     235              :   {
     236            0 :   case 0x8:
     237            0 :     GNUNET_buffer_write (buf,
     238              :                          "\\b",
     239              :                          2);
     240            0 :     break;
     241            0 :   case 0x9:
     242            0 :     GNUNET_buffer_write (buf,
     243              :                          "\\t",
     244              :                          2);
     245            0 :     break;
     246            0 :   case 0xA:
     247            0 :     GNUNET_buffer_write (buf,
     248              :                          "\\n",
     249              :                          2);
     250            0 :     break;
     251            0 :   case 0xC:
     252            0 :     GNUNET_buffer_write (buf,
     253              :                          "\\f",
     254              :                          2);
     255            0 :     break;
     256            0 :   case 0xD:
     257            0 :     GNUNET_buffer_write (buf,
     258              :                          "\\r",
     259              :                          2);
     260            0 :     break;
     261            3 :   default:
     262            3 :     GNUNET_snprintf (scratch,
     263              :                      sizeof (scratch),
     264              :                      "\\u%04x",
     265              :                      (unsigned int) val);
     266            3 :     GNUNET_buffer_write (buf,
     267              :                          scratch,
     268              :                          6);
     269            3 :     break;
     270              :   }
     271            3 : }
     272              : 
     273              : 
     274              : size_t
     275          617 : TALER_rfc8785encode (char **inp)
     276              : {
     277          617 :   struct GNUNET_Buffer buf = { 0 };
     278          617 :   size_t left = strlen (*inp) + 1;
     279              :   size_t olen;
     280          617 :   char *in = *inp;
     281          617 :   const char *pos = in;
     282              : 
     283          617 :   GNUNET_buffer_prealloc (&buf,
     284              :                           left + 40);
     285          617 :   buf.warn_grow = 0; /* disable, + 40 is just a wild guess */
     286              :   while (1)
     287        17287 :   {
     288        17904 :     int mbl = u8_mblen ((unsigned char *) pos,
     289              :                         left);
     290              :     unsigned char val;
     291              : 
     292        17904 :     if (0 == mbl)
     293          617 :       break;
     294        17287 :     val = (unsigned char) *pos;
     295        17287 :     if ( (1 == mbl) &&
     296              :          (val <= 0x1F) )
     297              :     {
     298              :       /* Should not happen, as input is produced by
     299              :        * JSON stringification */
     300            0 :       GNUNET_break (0);
     301            0 :       lowdump (&buf,
     302              :                val);
     303              :     }
     304        17287 :     else if ( (1 == mbl) && ('\\' == *pos) )
     305              :     {
     306            8 :       switch (*(pos + 1))
     307              :       {
     308            1 :       case '\\':
     309            1 :         mbl = 2;
     310            1 :         GNUNET_buffer_write (&buf,
     311              :                              pos,
     312              :                              mbl);
     313            1 :         break;
     314            3 :       case 'u':
     315              :         {
     316              :           unsigned int num;
     317              :           uint32_t n32;
     318              :           unsigned char res[8];
     319              :           size_t rlen;
     320              : 
     321            3 :           GNUNET_assert ( (1 ==
     322              :                            sscanf (pos + 2,
     323              :                                    "%4x",
     324              :                                    &num)) ||
     325              :                           (1 ==
     326              :                            sscanf (pos + 2,
     327              :                                    "%4X",
     328              :                                    &num)) );
     329            3 :           mbl = 6;
     330            3 :           n32 = (uint32_t) num;
     331            3 :           rlen = sizeof (res);
     332            3 :           u32_to_u8 (&n32,
     333              :                      1,
     334              :                      res,
     335              :                      &rlen);
     336            3 :           if ( (1 == rlen) &&
     337            3 :                (res[0] <= 0x1F) )
     338              :           {
     339            3 :             lowdump (&buf,
     340            3 :                      res[0]);
     341              :           }
     342              :           else
     343              :           {
     344            0 :             GNUNET_buffer_write (&buf,
     345              :                                  (const char *) res,
     346              :                                  rlen);
     347              :           }
     348              :         }
     349            3 :         break;
     350            4 :       default:
     351            4 :         mbl = 2;
     352            4 :         GNUNET_buffer_write (&buf,
     353              :                              pos,
     354              :                              mbl);
     355            4 :         break;
     356              :       }
     357              :     }
     358              :     else
     359              :     {
     360        17279 :       GNUNET_buffer_write (&buf,
     361              :                            pos,
     362              :                            mbl);
     363              :     }
     364        17287 :     left -= mbl;
     365        17287 :     pos += mbl;
     366              :   }
     367              : 
     368              :   /* 0-terminate buffer */
     369          617 :   GNUNET_buffer_write (&buf,
     370              :                        "",
     371              :                        1);
     372          617 :   GNUNET_free (in);
     373          617 :   *inp = GNUNET_buffer_reap (&buf,
     374              :                              &olen);
     375          617 :   return olen;
     376              : }
     377              : 
     378              : 
     379              : /**
     380              :  * Hash normalized @a j JSON object or array and
     381              :  * store the result in @a hc.
     382              :  *
     383              :  * @param j JSON to hash
     384              :  * @param[out] hc where to write the hash
     385              :  */
     386              : void
     387          436 : TALER_json_hash (const json_t *j,
     388              :                  struct GNUNET_HashCode *hc)
     389              : {
     390              :   char *cstr;
     391              :   size_t clen;
     392              : 
     393          436 :   cstr = json_dumps (j,
     394              :                      JSON_COMPACT | JSON_SORT_KEYS);
     395          436 :   GNUNET_assert (NULL != cstr);
     396          436 :   clen = TALER_rfc8785encode (&cstr);
     397          436 :   GNUNET_CRYPTO_hash (cstr,
     398              :                       clen,
     399              :                       hc);
     400          436 :   GNUNET_free (cstr);
     401          436 : }
     402              : 
     403              : 
     404              : char **
     405          370 : TALER_words_split (const char *command,
     406              :                    const char **extra_args)
     407              : {
     408          370 :   unsigned int i = 0;
     409          370 :   unsigned int j = 0;
     410          370 :   unsigned int n = 0;
     411          370 :   char **res = NULL;
     412              : 
     413              :   /* Result is always NULL-terminated */
     414          370 :   GNUNET_array_append (res, n, NULL);
     415              : 
     416              :   /* Split command into words */
     417              :   while (1)
     418          370 :   {
     419              :     char *c;
     420              : 
     421              :     /* Skip initial whitespace before word */
     422          740 :     while (' ' == command[i])
     423            0 :       i++;
     424              : 
     425              :     /* Start of new word */
     426          740 :     j = i;
     427              : 
     428              :     /* Scan to end of word */
     429        14558 :     while ( (0 != command[j]) && (' ' != command[j]) )
     430        13818 :       j++;
     431              : 
     432              :     /* No new word found */
     433          740 :     if (i == j)
     434          370 :       break;
     435              : 
     436              :     /* Append word to result */
     437          370 :     c = GNUNET_malloc (j - i + 1);
     438          370 :     memcpy (c, &command[i], j - i);
     439          370 :     c[j - i] = 0;
     440          370 :     res[n - 1] = c;
     441          370 :     GNUNET_array_append (res, n, NULL);
     442              : 
     443              :     /* Continue at end of word */
     444          370 :     i = j;
     445              :   }
     446              : 
     447              :   /* Append extra args */
     448          370 :   if (NULL != extra_args)
     449              :   {
     450         1470 :     for (const char **m = extra_args; *m; m++)
     451              :     {
     452         1100 :       res[n - 1] = GNUNET_strdup (*m);
     453         1100 :       GNUNET_array_append (res,
     454              :                            n,
     455              :                            NULL);
     456              :     }
     457              :   }
     458              : 
     459          370 :   return res;
     460              : }
     461              : 
     462              : 
     463              : void
     464           10 : TALER_words_destroy (char **args)
     465              : {
     466           10 :   if (NULL == args)
     467            0 :     return;
     468           40 :   for (char **m = args; *m; m++)
     469              :   {
     470           30 :     GNUNET_free (*m);
     471           30 :     *m = NULL;
     472              :   }
     473           10 :   GNUNET_free (args);
     474              : }
     475              : 
     476              : 
     477              : bool
     478           32 : TALER_is_slug (const char *slug)
     479              : {
     480              :   const unsigned char *p;
     481              : 
     482           32 :   if ('\0' == slug[0])
     483            1 :     return false;
     484              : 
     485              :   /* Reject special path components */
     486           31 :   if (0 == strcmp (slug, ".") ||
     487           30 :       0 == strcmp (slug, ".."))
     488            2 :     return false;
     489              : 
     490          191 :   for (p = (const unsigned char *) slug; '\0' != *p; p++)
     491              :   {
     492          171 :     unsigned char c = *p;
     493              : 
     494          171 :     if (isalnum (c))
     495          152 :       continue;
     496              : 
     497           19 :     switch (c)
     498              :     {
     499           10 :     case '-':
     500              :     case '_':
     501              :     case '.':
     502              :     case ':':
     503              :     case '~':
     504              :       /* Note: '~' is needed as libeufin-bank allows it in account
     505              :          usernames, which the core banking API exposes as slugs. */
     506           10 :       continue;
     507            9 :     default:
     508            9 :       return false;
     509              :     }
     510              :   }
     511              : 
     512           20 :   return true;
     513              : }
     514              : 
     515              : 
     516              : bool
     517           17 : TALER_is_session_id (const char *session_id)
     518              : {
     519              :   const unsigned char *p;
     520              : 
     521              :   /* The empty session ID is legal, it means "no session". */
     522           17 :   if ('\0' == session_id[0])
     523            1 :     return true;
     524              : 
     525              :   /* Reject special path components, session IDs are used
     526              :      as path components of "taler://pay/" URIs. */
     527           16 :   if ( (0 == strcmp (session_id,
     528           15 :                      ".")) ||
     529           15 :        (0 == strcmp (session_id,
     530              :                      "..")) )
     531            2 :     return false;
     532              : 
     533          104 :   for (p = (const unsigned char *) session_id; '\0' != *p; p++)
     534              :   {
     535           98 :     unsigned char c = *p;
     536              : 
     537           98 :     if (isalnum (c))
     538           79 :       continue;
     539              : 
     540           19 :     switch (c)
     541              :     {
     542           11 :     case '-':
     543              :     case '_':
     544              :     case '.':
     545              :     case ':':
     546              :     case '=':
     547              :     case '~':
     548              :       /* Note: unlike for slugs, '=' is allowed here as
     549              :          base64-encoded identifiers (such as the Paivana IDs
     550              :          used as merchant session IDs) may end in padding. */
     551           11 :       continue;
     552            8 :     default:
     553            8 :       return false;
     554              :     }
     555              :   }
     556              : 
     557            6 :   return true;
     558              : }
     559              : 
     560              : 
     561              : #ifdef __APPLE__
     562              : char *
     563              : strchrnul (const char *s,
     564              :            int c)
     565              : {
     566              :   char *value;
     567              :   value = strchr (s,
     568              :                   c);
     569              :   if (NULL == value)
     570              :     value = &s[strlen (s)];
     571              :   return value;
     572              : }
     573              : 
     574              : 
     575              : #endif
     576              : 
     577              : 
     578              : /* end of util.c */
        

Generated by: LCOV version 2.0-1