LCOV - code coverage report
Current view: top level - util - payto.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 225 315 71.4 %
Date: 2025-06-05 21:03:14 Functions: 20 24 83.3 %

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

Generated by: LCOV version 1.16