LCOV - code coverage report
Current view: top level - util - payto.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 56.4 % 330 186
Test Date: 2026-09-11 18:55:36 Functions: 79.2 % 24 19

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2019-2024 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 payto.c
      18              :  * @brief Common utility functions for dealing with payto://-URIs
      19              :  * @author Florian Dold
      20              :  */
      21              : #include "platform.h"  /* UNNECESSARY? */
      22              : #include "taler/taler_util.h"
      23              : 
      24              : 
      25              : /**
      26              :  * Prefix of PAYTO URLs.
      27              :  */
      28              : #define PAYTO "payto://"
      29              : 
      30              : 
      31              : int
      32           25 : TALER_full_payto_cmp (const struct TALER_FullPayto a,
      33              :                       const struct TALER_FullPayto b)
      34              : {
      35           25 :   if ( (NULL == a.full_payto) &&
      36            0 :        (NULL == b.full_payto) )
      37            0 :     return 0;
      38           25 :   if (NULL == a.full_payto)
      39            0 :     return -1;
      40           25 :   if (NULL == b.full_payto)
      41            0 :     return 1;
      42           25 :   return strcmp (a.full_payto,
      43           25 :                  b.full_payto);
      44              : }
      45              : 
      46              : 
      47              : bool
      48          356 : TALER_payto_is_wallet (const char *payto_uri)
      49              : {
      50              :   return
      51          356 :     (0 == strncasecmp (payto_uri,
      52              :                        "payto://taler-reserve/",
      53          712 :                        strlen ("payto://taler-reserve/"))) ||
      54          356 :     (0 == strncasecmp (payto_uri,
      55              :                        "payto://taler-reserve-http/",
      56              :                        strlen ("payto://taler-reserve-http/")));
      57              : }
      58              : 
      59              : 
      60              : int
      61            4 : TALER_normalized_payto_cmp (const struct TALER_NormalizedPayto a,
      62              :                             const struct TALER_NormalizedPayto b)
      63              : {
      64            4 :   if ( (NULL == a.normalized_payto) &&
      65            0 :        (NULL == b.normalized_payto) )
      66            0 :     return 0;
      67            4 :   if (NULL == a.normalized_payto)
      68            0 :     return -1;
      69            4 :   if (NULL == b.normalized_payto)
      70            0 :     return 1;
      71            4 :   return strcmp (a.normalized_payto,
      72            4 :                  b.normalized_payto);
      73              : }
      74              : 
      75              : 
      76              : void
      77          422 : TALER_full_payto_normalize_and_hash (const struct TALER_FullPayto in,
      78              :                                      struct TALER_NormalizedPaytoHashP *out)
      79              : {
      80              :   struct TALER_NormalizedPayto normalized_payto_uri;
      81              : 
      82              :   normalized_payto_uri
      83          422 :     = TALER_payto_normalize (in);
      84          422 :   if (NULL == normalized_payto_uri.normalized_payto)
      85              :   {
      86              :     /* Callers must have validated @a in; never crash on a URI that
      87              :        slipped through. */
      88            0 :     GNUNET_break (0);
      89            0 :     memset (out,
      90              :             0,
      91              :             sizeof (*out));
      92            0 :     return;
      93              :   }
      94          422 :   TALER_normalized_payto_hash (normalized_payto_uri,
      95              :                                out);
      96          422 :   GNUNET_free (normalized_payto_uri.normalized_payto);
      97              : }
      98              : 
      99              : 
     100              : /**
     101              :  * Compare two full payto URIs for equality in their normalized form.
     102              :  *
     103              :  * @param a a full payto URI, NULL is permitted
     104              :  * @param b a full payto URI, NULL is permitted
     105              :  * @return 0 if both are equal, otherwise -1 or 1
     106              :  */
     107              : int
     108            4 : TALER_full_payto_normalize_and_cmp (const struct TALER_FullPayto a,
     109              :                                     const struct TALER_FullPayto b)
     110              : {
     111              :   struct TALER_NormalizedPayto an
     112            4 :     = TALER_payto_normalize (a);
     113              :   struct TALER_NormalizedPayto bn
     114            4 :     = TALER_payto_normalize (b);
     115              :   int ret;
     116              : 
     117            4 :   ret = TALER_normalized_payto_cmp (an,
     118              :                                     bn);
     119            4 :   GNUNET_free (an.normalized_payto);
     120            4 :   GNUNET_free (bn.normalized_payto);
     121            4 :   return ret;
     122              : }
     123              : 
     124              : 
     125              : /**
     126              :  * Extract the value under @a key from the URI parameters.
     127              :  *
     128              :  * @param fpayto_uri the full payto URL to parse
     129              :  * @param search_key key to look for, including "="
     130              :  * @return NULL if the @a key parameter is not found.
     131              :  *         The caller should free the returned value.
     132              :  */
     133              : static char *
     134         1906 : payto_get_key (const struct TALER_FullPayto fpayto_uri,
     135              :                const char *search_key)
     136              : {
     137         1906 :   const char *payto_uri = fpayto_uri.full_payto;
     138              :   const char *key;
     139              :   const char *value_start;
     140              :   const char *value_end;
     141              : 
     142         1906 :   key = strchr (payto_uri,
     143              :                 (unsigned char) '?');
     144         1906 :   if (NULL == key)
     145            1 :     return NULL;
     146              : 
     147              :   do {
     148         1905 :     if (0 == strncasecmp (++key,
     149              :                           search_key,
     150              :                           strlen (search_key)))
     151              :     {
     152         1905 :       value_start = strchr (key,
     153              :                             (unsigned char) '=');
     154         1905 :       if (NULL == value_start)
     155            0 :         return NULL;
     156         1905 :       value_end = strchrnul (value_start,
     157              :                              (unsigned char) '&');
     158              : 
     159         1905 :       return GNUNET_strndup (value_start + 1,
     160              :                              value_end - value_start - 1);
     161              :     }
     162            0 :   } while ( (key = strchr (key,
     163              :                            (unsigned char) '&')) );
     164            0 :   return NULL;
     165              : }
     166              : 
     167              : 
     168              : char *
     169            2 : TALER_payto_get_subject (const struct TALER_FullPayto payto_uri)
     170              : {
     171            2 :   return payto_get_key (payto_uri,
     172              :                         "subject=");
     173              : }
     174              : 
     175              : 
     176              : char *
     177          990 : TALER_payto_get_method (const char *payto_uri)
     178              : {
     179              :   const char *start;
     180              :   const char *end;
     181              : 
     182          990 :   if (0 != strncasecmp (payto_uri,
     183              :                         PAYTO,
     184              :                         strlen (PAYTO)))
     185            0 :     return NULL;
     186          990 :   start = &payto_uri[strlen (PAYTO)];
     187          990 :   end = strchr (start,
     188              :                 (unsigned char) '/');
     189          990 :   if (NULL == end)
     190            0 :     return NULL;
     191          990 :   return GNUNET_strndup (start,
     192              :                          end - start);
     193              : }
     194              : 
     195              : 
     196              : char *
     197          335 : TALER_xtalerbank_account_from_payto (const struct TALER_FullPayto payto)
     198              : {
     199              :   const char *host;
     200              :   const char *beg;
     201              :   const char *nxt;
     202              :   const char *end;
     203              : 
     204          335 :   if (0 != strncasecmp (payto.full_payto,
     205              :                         PAYTO "x-taler-bank/",
     206              :                         strlen (PAYTO "x-taler-bank/")))
     207              :   {
     208            0 :     GNUNET_break_op (0);
     209            0 :     return NULL;
     210              :   }
     211          335 :   host = &payto.full_payto[strlen (PAYTO "x-taler-bank/")];
     212          335 :   beg = strchr (host,
     213              :                 '/');
     214          335 :   if (NULL == beg)
     215              :   {
     216            0 :     GNUNET_break_op (0);
     217            0 :     return NULL;
     218              :   }
     219          335 :   beg++; /* now points to $ACCOUNT or $PATH */
     220          335 :   nxt = strchr (beg,
     221              :                 '/');
     222          335 :   end = strchr (beg,
     223              :                 '?');
     224          335 :   if (NULL == end)
     225            0 :     end = &beg[strlen (beg)];
     226          337 :   while ( (NULL != nxt) &&
     227            3 :           (end - nxt > 0) )
     228              :   {
     229            2 :     beg = nxt + 1;
     230            2 :     nxt = strchr (beg,
     231              :                   '/');
     232              :   }
     233          335 :   return GNUNET_strndup (beg,
     234              :                          end - beg);
     235              : }
     236              : 
     237              : 
     238              : /**
     239              :  * Validate payto://iban/ account URL (only account information,
     240              :  * wire subject and amount are ignored).
     241              :  *
     242              :  * @param payto_uri payto URL to parse
     243              :  * @return NULL on success, otherwise an error message
     244              :  *      to be freed by the caller
     245              :  */
     246              : static char *
     247         1916 : validate_payto_iban (const char *payto_uri)
     248              : {
     249         1916 :   size_t slen = strlen (payto_uri);
     250              :   const char *iban;
     251              :   const char *q;
     252              :   char *result;
     253              :   char *err;
     254              : 
     255              : #define IBAN_PREFIX "payto://iban/"
     256         1916 :   if (0 != strncasecmp (payto_uri,
     257              :                         IBAN_PREFIX,
     258              :                         strlen (IBAN_PREFIX)))
     259         1916 :     return NULL; /* not an IBAN */
     260            0 :   q = memchr (payto_uri,
     261              :               '?',
     262              :               slen);
     263              :   /* RFC 8905, Section 7.3: the path is either "$IBAN" or "$BIC/$IBAN".
     264              :      Reject anything else here, so that normalize_payto_iban() -- which
     265              :      enforces the same rule by returning NULL -- can never be handed a
     266              :      URI that passed validation. */
     267              :   {
     268            0 :     unsigned int sc = 0;
     269            0 :     size_t plen = (NULL == q)
     270              :                   ? slen
     271            0 :                   : (size_t) (q - payto_uri);
     272              : 
     273            0 :     for (size_t i = 0; i < plen; i++)
     274            0 :       if ('/' == payto_uri[i])
     275            0 :         sc++;
     276            0 :     if ( (sc < 3) ||
     277              :          (sc > 4) )
     278            0 :       return GNUNET_strdup (
     279              :         "'iban' payto:// URI must have one or two path components");
     280              :   }
     281              :   /* Find the last '/' that is part of the path, i.e. before any '?'
     282              :      query part ('/' is a legal character in a query value and must
     283              :      not be mistaken for the path separator). */
     284              :   {
     285              :     const char *slash;
     286              : 
     287            0 :     slash = memrchr (payto_uri,
     288              :                      '/',
     289              :                      (NULL == q)
     290              :                      ? slen
     291            0 :                      : q - payto_uri);
     292            0 :     GNUNET_assert (NULL != slash); /* prefix guarantees a '/' */
     293            0 :     iban = slash + 1;
     294              :   }
     295              : #undef IBAN_PREFIX
     296            0 :   if (NULL != q)
     297              :   {
     298            0 :     result = GNUNET_strndup (iban,
     299              :                              q - iban);
     300              :   }
     301              :   else
     302              :   {
     303            0 :     result = GNUNET_strdup (iban);
     304              :   }
     305            0 :   if (NULL !=
     306            0 :       (err = TALER_iban_validate (result)))
     307              :   {
     308            0 :     GNUNET_free (result);
     309            0 :     return err;
     310              :   }
     311            0 :   GNUNET_free (result);
     312            0 :   return NULL;
     313              : }
     314              : 
     315              : 
     316              : /**
     317              :  * Validate payto://x-taler-bank/ account URL (only account information,
     318              :  * wire subject and amount are ignored).
     319              :  *
     320              :  * @param payto_uri payto URL to parse
     321              :  * @return NULL on success, otherwise an error message
     322              :  *      to be freed by the caller
     323              :  */
     324              : static char *
     325         1916 : validate_payto_xtalerbank (const char *payto_uri)
     326              : {
     327              :   const char *user;
     328              :   const char *nxt;
     329              :   const char *beg;
     330              :   const char *end;
     331              :   const char *host;
     332              :   bool dot_ok;
     333              :   bool post_colon;
     334              :   bool port_ok;
     335              : 
     336              : #define XTALERBANK_PREFIX PAYTO "x-taler-bank/"
     337         1916 :   if (0 != strncasecmp (payto_uri,
     338              :                         XTALERBANK_PREFIX,
     339              :                         strlen (XTALERBANK_PREFIX)))
     340            6 :     return NULL; /* not an x-taler-bank URI */
     341         1910 :   host = &payto_uri[strlen (XTALERBANK_PREFIX)];
     342              : #undef XTALERBANK_PREFIX
     343         1910 :   beg = strchr (host,
     344              :                 '/');
     345         1910 :   if (NULL == beg)
     346              :   {
     347            0 :     return GNUNET_strdup ("account name missing");
     348              :   }
     349         1910 :   beg++; /* now points to $ACCOUNT or $PATH */
     350         1910 :   nxt = strchr (beg,
     351              :                 '/');
     352         1910 :   end = strchr (beg,
     353              :                 '?');
     354         1910 :   if (NULL == end)
     355              :   {
     356            1 :     return GNUNET_strdup ("'receiver-name' parameter missing");
     357              :   }
     358         1916 :   while ( (NULL != nxt) &&
     359            8 :           (end - nxt > 0) )
     360              :   {
     361            7 :     beg = nxt + 1;
     362            7 :     nxt = strchr (beg,
     363              :                   '/');
     364              :   }
     365         1909 :   user = beg;
     366         1909 :   if (user == host + 1)
     367              :   {
     368            0 :     return GNUNET_strdup ("domain name missing");
     369              :   }
     370         1909 :   if ('-' == host[0])
     371            1 :     return GNUNET_strdup ("invalid character '-' at start of domain name");
     372         1908 :   dot_ok = false;
     373         1908 :   post_colon = false;
     374         1908 :   port_ok = false;
     375        19080 :   while (host != user)
     376              :   {
     377        19080 :     char c = host[0];
     378              : 
     379        19080 :     if ('/' == c)
     380              :     {
     381              :       /* path started, do not care about characters
     382              :          in the path */
     383         1905 :       break;
     384              :     }
     385        17175 :     if (':' == c)
     386              :     {
     387            4 :       post_colon = true;
     388            4 :       host++;
     389            4 :       continue;
     390              :     }
     391        17171 :     if (post_colon)
     392              :     {
     393           10 :       if (! ( ('0' <= c) && ('9' >= c) ) )
     394              :       {
     395              :         char *err;
     396              : 
     397            1 :         GNUNET_asprintf (&err,
     398              :                          "invalid character '%c' in port",
     399              :                          c);
     400            1 :         return err;
     401              :       }
     402            9 :       port_ok = true;
     403              :     }
     404              :     else
     405              :     {
     406        17161 :       if ('.' == c)
     407              :       {
     408            4 :         if (! dot_ok)
     409            2 :           return GNUNET_strdup ("invalid domain name (misplaced '.')");
     410            2 :         dot_ok = false;
     411              :       }
     412              :       else
     413              :       {
     414        17157 :         if (! ( ('-' == c) ||
     415        17156 :                 ('_' == c) ||
     416        17156 :                 ( ('0' <= c) && ('9' >= c) ) ||
     417        17156 :                 ( ('a' <= c) && ('z' >= c) ) ||
     418            0 :                 ( ('A' <= c) && ('Z' >= c) ) ) )
     419              :         {
     420              :           char *err;
     421              : 
     422            0 :           GNUNET_asprintf (&err,
     423              :                            "invalid character '%c' in domain name",
     424              :                            c);
     425            0 :           return err;
     426              :         }
     427        17157 :         dot_ok = true;
     428              :       }
     429              :     }
     430        17168 :     host++;
     431              :   }
     432         1905 :   if (post_colon && (! port_ok) )
     433              :   {
     434            1 :     return GNUNET_strdup ("port missing after ':'");
     435              :   }
     436         1904 :   return NULL;
     437              : }
     438              : 
     439              : 
     440              : /**
     441              :  * Generic validation of a payto:// URI. Checks the prefix
     442              :  * and character set.
     443              :  *
     444              :  * @param payto_uri URI to validate
     445              :  * @return NULL on success, otherwise an error message
     446              :  */
     447              : static char *
     448         1916 : payto_validate (const char *payto_uri)
     449              : {
     450              :   char *ret;
     451              :   const char *start;
     452              :   const char *end;
     453              : 
     454         1916 :   if (0 != strncasecmp (payto_uri,
     455              :                         PAYTO,
     456              :                         strlen (PAYTO)))
     457            0 :     return GNUNET_strdup ("invalid prefix");
     458        98789 :   for (unsigned int i = 0; '\0' != payto_uri[i]; i++)
     459              :   {
     460              :     /* This is more strict than RFC 8905, alas we do not need to support messages/instructions/etc.,
     461              :        and it is generally better to start with a narrow whitelist; we can be more permissive later ...*/
     462              : #define ALLOWED_CHARACTERS \
     463              :         "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:$&?!-_.,;=*+%~@()[]"
     464        96873 :     if (NULL == strchr (ALLOWED_CHARACTERS,
     465        96873 :                         (int) payto_uri[i]))
     466              :     {
     467            0 :       GNUNET_asprintf (&ret,
     468              :                        "Encountered invalid character `%c' at offset %u in payto URI `%s'",
     469            0 :                        payto_uri[i],
     470              :                        i,
     471              :                        payto_uri);
     472            0 :       return ret;
     473              :     }
     474              : #undef ALLOWED_CHARACTERS
     475              :   }
     476              : 
     477         1916 :   start = &payto_uri[strlen (PAYTO)];
     478         1916 :   end = strchr (start,
     479              :                 (unsigned char) '/');
     480         1916 :   if (NULL == end)
     481            0 :     return GNUNET_strdup ("missing '/' in payload");
     482              : 
     483         1916 :   if (NULL != (ret = validate_payto_iban (payto_uri)))
     484            0 :     return ret; /* got a definitive answer */
     485         1916 :   if (NULL != (ret = validate_payto_xtalerbank (payto_uri)))
     486            6 :     return ret; /* got a definitive answer */
     487              : 
     488              :   /* Insert validation calls for other bank account validation methods here! */
     489              : 
     490         1910 :   return NULL;
     491              : }
     492              : 
     493              : 
     494              : char *
     495            6 : TALER_normalized_payto_validate (const struct TALER_NormalizedPayto npayto_uri)
     496              : {
     497            6 :   const char *payto_uri = npayto_uri.normalized_payto;
     498              :   char *ret;
     499              : 
     500            6 :   ret = payto_validate (payto_uri);
     501            6 :   if (NULL != ret)
     502            0 :     return ret;
     503            6 :   return NULL;
     504              : }
     505              : 
     506              : 
     507              : char *
     508         1910 : TALER_payto_validate (const struct TALER_FullPayto fpayto_uri)
     509              : {
     510         1910 :   const char *payto_uri = fpayto_uri.full_payto;
     511              :   char *ret;
     512              : 
     513         1910 :   ret = payto_validate (payto_uri);
     514         1910 :   if (NULL != ret)
     515            6 :     return ret;
     516              :   {
     517              :     char *target;
     518              : 
     519         1904 :     target = payto_get_key (fpayto_uri,
     520              :                             "receiver-name=");
     521         1904 :     if (NULL == target)
     522            0 :       return GNUNET_strdup ("'receiver-name' parameter missing");
     523         1904 :     GNUNET_free (target);
     524              :   }
     525              : 
     526         1904 :   return NULL;
     527              : }
     528              : 
     529              : 
     530              : char *
     531            0 : TALER_payto_get_receiver_name (const struct TALER_FullPayto fpayto)
     532              : {
     533              :   char *err;
     534              : 
     535            0 :   err = TALER_payto_validate (fpayto);
     536            0 :   if (NULL != err)
     537              :   {
     538            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     539              :                 "Invalid payto://-URI `%s': %s\n",
     540              :                 fpayto.full_payto,
     541              :                 err);
     542            0 :     GNUNET_free (err);
     543            0 :     return NULL;
     544              :   }
     545            0 :   return payto_get_key (fpayto,
     546              :                         "receiver-name=");
     547              : }
     548              : 
     549              : 
     550              : /**
     551              :  * Normalize "payto://x-taler-bank/$HOSTNAME/[$PATH/]$USERNAME"
     552              :  * URI in @a input.
     553              :  *
     554              :  * Converts to lower-case, except for [$PATH/]$USERNAME which
     555              :  * is case-sensitive.
     556              :  *
     557              :  * @param len number of bytes in @a input
     558              :  * @param input input URL
     559              :  * @return NULL on error, otherwise 0-terminated canonicalized URI.
     560              :  */
     561              : static char *
     562          469 : normalize_payto_x_taler_bank (size_t len,
     563              :                               const char input[static len])
     564          469 : {
     565          469 :   char *res = GNUNET_malloc (len + 1);
     566          469 :   unsigned int sc = 0;
     567              : 
     568        16139 :   for (unsigned int i = 0; i<len; i++)
     569              :   {
     570        15670 :     char c = input[i];
     571              : 
     572        15670 :     if ('/' == c)
     573         1876 :       sc++;
     574        15670 :     if (sc < 4)
     575        14070 :       res[i] = (char) tolower ((int) c);
     576              :     else
     577         1600 :       res[i] = c;
     578              :   }
     579          469 :   return res;
     580              : }
     581              : 
     582              : 
     583              : /**
     584              :  * Normalize "payto://iban[/$BIC]/$IBAN"
     585              :  * URI in @a input.
     586              :  *
     587              :  * Removes $BIC (if present) and converts $IBAN to upper-case and prefix to
     588              :  * lower-case.
     589              :  *
     590              :  * @param len number of bytes in @a input
     591              :  * @param input input URL
     592              :  * @return NULL on error, otherwise 0-terminated canonicalized URI.
     593              :  */
     594              : static char *
     595            0 : normalize_payto_iban (size_t len,
     596              :                       const char input[static len])
     597            0 : {
     598              :   char *res;
     599            0 :   size_t pos = 0;
     600            0 :   unsigned int sc = 0;
     601              :   bool have_bic;
     602              : 
     603            0 :   for (unsigned int i = 0; i<len; i++)
     604            0 :     if ('/' == input[i])
     605            0 :       sc++;
     606            0 :   if ( (sc > 4) ||
     607              :        (sc < 3) )
     608              :   {
     609            0 :     GNUNET_break (0);
     610            0 :     return NULL;
     611              :   }
     612            0 :   have_bic = (4 == sc);
     613            0 :   res = GNUNET_malloc (len + 1);
     614            0 :   sc = 0;
     615            0 :   for (unsigned int i = 0; i<len; i++)
     616              :   {
     617            0 :     char c = input[i];
     618              : 
     619            0 :     if ('/' == c)
     620            0 :       sc++;
     621            0 :     switch (sc)
     622              :     {
     623            0 :     case 0: /* payto: */
     624              :     case 1: /* / */
     625              :     case 2: /* /iban */
     626            0 :       res[pos++] = (char) tolower ((int) c);
     627            0 :       break;
     628            0 :     case 3: /* /$BIC or /$IBAN */
     629            0 :       if (have_bic)
     630            0 :         continue;
     631            0 :       res[pos++] = (char) toupper ((int) c);
     632            0 :       break;
     633            0 :     case 4: /* /$IBAN */
     634            0 :       res[pos++] = (char) toupper ((int) c);
     635            0 :       break;
     636              :     }
     637              :   }
     638            0 :   GNUNET_assert (pos <= len);
     639            0 :   return res;
     640              : }
     641              : 
     642              : 
     643              : /**
     644              :  * Normalize "payto://upi/$EMAIL"
     645              :  * URI in @a input.
     646              :  *
     647              :  * Converts to lower-case.
     648              :  *
     649              :  * @param len number of bytes in @a input
     650              :  * @param input input URL
     651              :  * @return NULL on error, otherwise 0-terminated canonicalized URI.
     652              :  */
     653              : static char *
     654            0 : normalize_payto_upi (size_t len,
     655              :                      const char input[static len])
     656            0 : {
     657            0 :   char *res = GNUNET_malloc (len + 1);
     658              : 
     659            0 :   for (unsigned int i = 0; i<len; i++)
     660              :   {
     661            0 :     char c = input[i];
     662              : 
     663            0 :     res[i] = (char) tolower ((int) c);
     664              :   }
     665            0 :   return res;
     666              : }
     667              : 
     668              : 
     669              : /**
     670              :  * Normalize "payto://bitcoin/$ADDRESS"
     671              :  * URI in @a input.
     672              :  *
     673              :  * Converts to lower-case, except for $ADDRESS which
     674              :  * is case-sensitive.
     675              :  *
     676              :  * @param len number of bytes in @a input
     677              :  * @param input input URL
     678              :  * @return NULL on error, otherwise 0-terminated canonicalized URI.
     679              :  */
     680              : static char *
     681            0 : normalize_payto_bitcoin (size_t len,
     682              :                          const char input[static len])
     683            0 : {
     684            0 :   char *res = GNUNET_malloc (len + 1);
     685            0 :   unsigned int sc = 0;
     686              : 
     687            0 :   for (unsigned int i = 0; i<len; i++)
     688              :   {
     689            0 :     char c = input[i];
     690              : 
     691            0 :     if ('/' == c)
     692            0 :       sc++;
     693            0 :     if (sc < 3)
     694            0 :       res[i] = (char) tolower ((int) c);
     695              :     else
     696            0 :       res[i] = c;
     697              :   }
     698            0 :   return res;
     699              : }
     700              : 
     701              : 
     702              : /**
     703              :  * Normalize "payto://ilp/$NAME"
     704              :  * URI in @a input.
     705              :  *
     706              :  * Converts to lower-case.
     707              :  *
     708              :  * @param len number of bytes in @a input
     709              :  * @param input input URL
     710              :  * @return NULL on error, otherwise 0-terminated canonicalized URI.
     711              :  */
     712              : static char *
     713            0 : normalize_payto_ilp (size_t len,
     714              :                      const char input[static len])
     715            0 : {
     716            0 :   char *res = GNUNET_malloc (len + 1);
     717              : 
     718            0 :   for (unsigned int i = 0; i<len; i++)
     719              :   {
     720            0 :     char c = input[i];
     721              : 
     722            0 :     res[i] = (char) tolower ((int) c);
     723              :   }
     724            0 :   return res;
     725              : }
     726              : 
     727              : 
     728              : struct TALER_NormalizedPayto
     729          469 : TALER_payto_normalize (const struct TALER_FullPayto input)
     730              : {
     731          469 :   struct TALER_NormalizedPayto npto = {
     732              :     .normalized_payto = NULL
     733              :   };
     734              :   char *method;
     735              :   const char *end;
     736              :   char *ret;
     737              : 
     738              :   {
     739              :     char *err;
     740              : 
     741          469 :     err = TALER_payto_validate (input);
     742          469 :     if (NULL != err)
     743              :     {
     744            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     745              :                   "Malformed payto://-URI `%s': %s\n",
     746              :                   input.full_payto,
     747              :                   err);
     748            0 :       GNUNET_free (err);
     749            0 :       return npto;
     750              :     }
     751              :   }
     752          469 :   method = TALER_payto_get_method (input.full_payto);
     753          469 :   if (NULL == method)
     754              :   {
     755            0 :     GNUNET_break (0);
     756            0 :     return npto;
     757              :   }
     758          469 :   end = strchr (input.full_payto,
     759              :                 '?');
     760          469 :   if (NULL == end)
     761            0 :     end = &input.full_payto[strlen (input.full_payto)];
     762          469 :   if (0 == strcasecmp (method,
     763              :                        "x-taler-bank"))
     764          469 :     ret = normalize_payto_x_taler_bank (end - input.full_payto,
     765          469 :                                         input.full_payto);
     766            0 :   else if (0 == strcasecmp (method,
     767              :                             "iban"))
     768            0 :     ret = normalize_payto_iban (end - input.full_payto,
     769            0 :                                 input.full_payto);
     770            0 :   else if (0 == strcasecmp (method,
     771              :                             "upi"))
     772            0 :     ret = normalize_payto_upi (end - input.full_payto,
     773            0 :                                input.full_payto);
     774            0 :   else if (0 == strcasecmp (method,
     775              :                             "bitcoin"))
     776            0 :     ret = normalize_payto_bitcoin (end - input.full_payto,
     777            0 :                                    input.full_payto);
     778            0 :   else if (0 == strcasecmp (method,
     779              :                             "ilp"))
     780            0 :     ret = normalize_payto_ilp (end - input.full_payto,
     781            0 :                                input.full_payto);
     782              :   else
     783            0 :     ret = GNUNET_strndup (input.full_payto,
     784              :                           end - input.full_payto);
     785          469 :   GNUNET_free (method);
     786          469 :   npto.normalized_payto = ret;
     787          469 :   return npto;
     788              : }
     789              : 
     790              : 
     791              : void
     792          584 : TALER_normalized_payto_hash (const struct TALER_NormalizedPayto npayto,
     793              :                              struct TALER_NormalizedPaytoHashP *h_npayto)
     794              : {
     795              :   struct GNUNET_HashCode sha512;
     796              : 
     797              :   GNUNET_static_assert (sizeof (sha512) > sizeof (*h_npayto));
     798          584 :   GNUNET_CRYPTO_hash (npayto.normalized_payto,
     799          584 :                       strlen (npayto.normalized_payto) + 1,
     800              :                       &sha512);
     801              :   /* truncate */
     802          584 :   GNUNET_memcpy (h_npayto,
     803              :                  &sha512,
     804              :                  sizeof (*h_npayto));
     805          584 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     806              :               "Normalized hash of normalized payto `%s' is %16s\n",
     807              :               npayto.normalized_payto,
     808              :               GNUNET_h2s_full (&sha512));
     809          584 : }
     810              : 
     811              : 
     812              : void
     813          568 : TALER_full_payto_hash (const struct TALER_FullPayto fpayto,
     814              :                        struct TALER_FullPaytoHashP *h_fpayto)
     815              : {
     816              :   struct GNUNET_HashCode sha512;
     817              : 
     818              :   GNUNET_static_assert (sizeof (sha512) > sizeof (*h_fpayto));
     819          568 :   GNUNET_CRYPTO_hash (fpayto.full_payto,
     820          568 :                       strlen (fpayto.full_payto) + 1,
     821              :                       &sha512);
     822              :   /* truncate */
     823          568 :   GNUNET_memcpy (h_fpayto,
     824              :                  &sha512,
     825              :                  sizeof (*h_fpayto));
     826          568 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     827              :               "Full hash of full payto `%s' is %16s\n",
     828              :               fpayto.full_payto,
     829              :               GNUNET_h2s_full (&sha512));
     830          568 : }
     831              : 
     832              : 
     833              : struct TALER_NormalizedPayto
     834          179 : TALER_reserve_make_payto (const char *exchange_url,
     835              :                           const struct TALER_ReservePublicKeyP *reserve_pub)
     836              : {
     837          179 :   struct TALER_NormalizedPayto npto = {
     838              :     .normalized_payto = NULL
     839              :   };
     840              :   char pub_str[sizeof (*reserve_pub) * 2];
     841              :   char *end;
     842              :   bool is_http;
     843              :   char *reserve_url;
     844              : 
     845          179 :   end = GNUNET_STRINGS_data_to_string (
     846              :     reserve_pub,
     847              :     sizeof (*reserve_pub),
     848              :     pub_str,
     849              :     sizeof (pub_str));
     850          179 :   *end = '\0';
     851          179 :   if (0 == strncmp (exchange_url,
     852              :                     "http://",
     853              :                     strlen ("http://")))
     854              :   {
     855          179 :     is_http = true;
     856          179 :     exchange_url = &exchange_url[strlen ("http://")];
     857              :   }
     858            0 :   else if (0 == strncmp (exchange_url,
     859              :                          "https://",
     860              :                          strlen ("https://")))
     861              :   {
     862            0 :     is_http = false;
     863            0 :     exchange_url = &exchange_url[strlen ("https://")];
     864              :   }
     865              :   else
     866              :   {
     867            0 :     GNUNET_break (0);
     868            0 :     return npto;
     869              :   }
     870              :   /* exchange_url includes trailing '/' */
     871          179 :   GNUNET_asprintf (&reserve_url,
     872              :                    "payto://%s/%s%s",
     873              :                    is_http ? "taler-reserve-http" : "taler-reserve",
     874              :                    exchange_url,
     875              :                    pub_str);
     876          179 :   npto.normalized_payto = reserve_url;
     877          179 :   return npto;
     878              : }
     879              : 
     880              : 
     881              : /* end of payto.c */
        

Generated by: LCOV version 2.0-1