exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

payto.c (21730B)


      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 TALER_full_payto_cmp (const struct TALER_FullPayto a,
     33                       const struct TALER_FullPayto b)
     34 {
     35   if ( (NULL == a.full_payto) &&
     36        (NULL == b.full_payto) )
     37     return 0;
     38   if (NULL == a.full_payto)
     39     return -1;
     40   if (NULL == b.full_payto)
     41     return 1;
     42   return strcmp (a.full_payto,
     43                  b.full_payto);
     44 }
     45 
     46 
     47 bool
     48 TALER_payto_is_wallet (const char *payto_uri)
     49 {
     50   return
     51     (0 == strncasecmp (payto_uri,
     52                        "payto://taler-reserve/",
     53                        strlen ("payto://taler-reserve/"))) ||
     54     (0 == strncasecmp (payto_uri,
     55                        "payto://taler-reserve-http/",
     56                        strlen ("payto://taler-reserve-http/")));
     57 }
     58 
     59 
     60 int
     61 TALER_normalized_payto_cmp (const struct TALER_NormalizedPayto a,
     62                             const struct TALER_NormalizedPayto b)
     63 {
     64   if ( (NULL == a.normalized_payto) &&
     65        (NULL == b.normalized_payto) )
     66     return 0;
     67   if (NULL == a.normalized_payto)
     68     return -1;
     69   if (NULL == b.normalized_payto)
     70     return 1;
     71   return strcmp (a.normalized_payto,
     72                  b.normalized_payto);
     73 }
     74 
     75 
     76 void
     77 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     = TALER_payto_normalize (in);
     84   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     GNUNET_break (0);
     89     memset (out,
     90             0,
     91             sizeof (*out));
     92     return;
     93   }
     94   TALER_normalized_payto_hash (normalized_payto_uri,
     95                                out);
     96   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 TALER_full_payto_normalize_and_cmp (const struct TALER_FullPayto a,
    109                                     const struct TALER_FullPayto b)
    110 {
    111   struct TALER_NormalizedPayto an
    112     = TALER_payto_normalize (a);
    113   struct TALER_NormalizedPayto bn
    114     = TALER_payto_normalize (b);
    115   int ret;
    116 
    117   ret = TALER_normalized_payto_cmp (an,
    118                                     bn);
    119   GNUNET_free (an.normalized_payto);
    120   GNUNET_free (bn.normalized_payto);
    121   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 payto_get_key (const struct TALER_FullPayto fpayto_uri,
    135                const char *search_key)
    136 {
    137   const char *payto_uri = fpayto_uri.full_payto;
    138   const char *key;
    139   const char *value_start;
    140   const char *value_end;
    141 
    142   key = strchr (payto_uri,
    143                 (unsigned char) '?');
    144   if (NULL == key)
    145     return NULL;
    146 
    147   do {
    148     if (0 == strncasecmp (++key,
    149                           search_key,
    150                           strlen (search_key)))
    151     {
    152       value_start = strchr (key,
    153                             (unsigned char) '=');
    154       if (NULL == value_start)
    155         return NULL;
    156       value_end = strchrnul (value_start,
    157                              (unsigned char) '&');
    158 
    159       return GNUNET_strndup (value_start + 1,
    160                              value_end - value_start - 1);
    161     }
    162   } while ( (key = strchr (key,
    163                            (unsigned char) '&')) );
    164   return NULL;
    165 }
    166 
    167 
    168 char *
    169 TALER_payto_get_subject (const struct TALER_FullPayto payto_uri)
    170 {
    171   return payto_get_key (payto_uri,
    172                         "subject=");
    173 }
    174 
    175 
    176 char *
    177 TALER_payto_get_method (const char *payto_uri)
    178 {
    179   const char *start;
    180   const char *end;
    181 
    182   if (0 != strncasecmp (payto_uri,
    183                         PAYTO,
    184                         strlen (PAYTO)))
    185     return NULL;
    186   start = &payto_uri[strlen (PAYTO)];
    187   end = strchr (start,
    188                 (unsigned char) '/');
    189   if (NULL == end)
    190     return NULL;
    191   return GNUNET_strndup (start,
    192                          end - start);
    193 }
    194 
    195 
    196 char *
    197 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   if (0 != strncasecmp (payto.full_payto,
    205                         PAYTO "x-taler-bank/",
    206                         strlen (PAYTO "x-taler-bank/")))
    207   {
    208     GNUNET_break_op (0);
    209     return NULL;
    210   }
    211   host = &payto.full_payto[strlen (PAYTO "x-taler-bank/")];
    212   beg = strchr (host,
    213                 '/');
    214   if (NULL == beg)
    215   {
    216     GNUNET_break_op (0);
    217     return NULL;
    218   }
    219   beg++; /* now points to $ACCOUNT or $PATH */
    220   nxt = strchr (beg,
    221                 '/');
    222   end = strchr (beg,
    223                 '?');
    224   if (NULL == end)
    225     end = &beg[strlen (beg)];
    226   while ( (NULL != nxt) &&
    227           (end - nxt > 0) )
    228   {
    229     beg = nxt + 1;
    230     nxt = strchr (beg,
    231                   '/');
    232   }
    233   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 validate_payto_iban (const char *payto_uri)
    248 {
    249   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   if (0 != strncasecmp (payto_uri,
    257                         IBAN_PREFIX,
    258                         strlen (IBAN_PREFIX)))
    259     return NULL; /* not an IBAN */
    260   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     unsigned int sc = 0;
    269     size_t plen = (NULL == q)
    270                   ? slen
    271                   : (size_t) (q - payto_uri);
    272 
    273     for (size_t i = 0; i < plen; i++)
    274       if ('/' == payto_uri[i])
    275         sc++;
    276     if ( (sc < 3) ||
    277          (sc > 4) )
    278       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     slash = memrchr (payto_uri,
    288                      '/',
    289                      (NULL == q)
    290                      ? slen
    291                      : q - payto_uri);
    292     GNUNET_assert (NULL != slash); /* prefix guarantees a '/' */
    293     iban = slash + 1;
    294   }
    295 #undef IBAN_PREFIX
    296   if (NULL != q)
    297   {
    298     result = GNUNET_strndup (iban,
    299                              q - iban);
    300   }
    301   else
    302   {
    303     result = GNUNET_strdup (iban);
    304   }
    305   if (NULL !=
    306       (err = TALER_iban_validate (result)))
    307   {
    308     GNUNET_free (result);
    309     return err;
    310   }
    311   GNUNET_free (result);
    312   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 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   if (0 != strncasecmp (payto_uri,
    338                         XTALERBANK_PREFIX,
    339                         strlen (XTALERBANK_PREFIX)))
    340     return NULL; /* not an x-taler-bank URI */
    341   host = &payto_uri[strlen (XTALERBANK_PREFIX)];
    342 #undef XTALERBANK_PREFIX
    343   beg = strchr (host,
    344                 '/');
    345   if (NULL == beg)
    346   {
    347     return GNUNET_strdup ("account name missing");
    348   }
    349   beg++; /* now points to $ACCOUNT or $PATH */
    350   nxt = strchr (beg,
    351                 '/');
    352   end = strchr (beg,
    353                 '?');
    354   if (NULL == end)
    355   {
    356     return GNUNET_strdup ("'receiver-name' parameter missing");
    357   }
    358   while ( (NULL != nxt) &&
    359           (end - nxt > 0) )
    360   {
    361     beg = nxt + 1;
    362     nxt = strchr (beg,
    363                   '/');
    364   }
    365   user = beg;
    366   if (user == host + 1)
    367   {
    368     return GNUNET_strdup ("domain name missing");
    369   }
    370   if ('-' == host[0])
    371     return GNUNET_strdup ("invalid character '-' at start of domain name");
    372   dot_ok = false;
    373   post_colon = false;
    374   port_ok = false;
    375   while (host != user)
    376   {
    377     char c = host[0];
    378 
    379     if ('/' == c)
    380     {
    381       /* path started, do not care about characters
    382          in the path */
    383       break;
    384     }
    385     if (':' == c)
    386     {
    387       post_colon = true;
    388       host++;
    389       continue;
    390     }
    391     if (post_colon)
    392     {
    393       if (! ( ('0' <= c) && ('9' >= c) ) )
    394       {
    395         char *err;
    396 
    397         GNUNET_asprintf (&err,
    398                          "invalid character '%c' in port",
    399                          c);
    400         return err;
    401       }
    402       port_ok = true;
    403     }
    404     else
    405     {
    406       if ('.' == c)
    407       {
    408         if (! dot_ok)
    409           return GNUNET_strdup ("invalid domain name (misplaced '.')");
    410         dot_ok = false;
    411       }
    412       else
    413       {
    414         if (! ( ('-' == c) ||
    415                 ('_' == c) ||
    416                 ( ('0' <= c) && ('9' >= c) ) ||
    417                 ( ('a' <= c) && ('z' >= c) ) ||
    418                 ( ('A' <= c) && ('Z' >= c) ) ) )
    419         {
    420           char *err;
    421 
    422           GNUNET_asprintf (&err,
    423                            "invalid character '%c' in domain name",
    424                            c);
    425           return err;
    426         }
    427         dot_ok = true;
    428       }
    429     }
    430     host++;
    431   }
    432   if (post_colon && (! port_ok) )
    433   {
    434     return GNUNET_strdup ("port missing after ':'");
    435   }
    436   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 payto_validate (const char *payto_uri)
    449 {
    450   char *ret;
    451   const char *start;
    452   const char *end;
    453 
    454   if (0 != strncasecmp (payto_uri,
    455                         PAYTO,
    456                         strlen (PAYTO)))
    457     return GNUNET_strdup ("invalid prefix");
    458   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     if (NULL == strchr (ALLOWED_CHARACTERS,
    465                         (int) payto_uri[i]))
    466     {
    467       GNUNET_asprintf (&ret,
    468                        "Encountered invalid character `%c' at offset %u in payto URI `%s'",
    469                        payto_uri[i],
    470                        i,
    471                        payto_uri);
    472       return ret;
    473     }
    474 #undef ALLOWED_CHARACTERS
    475   }
    476 
    477   start = &payto_uri[strlen (PAYTO)];
    478   end = strchr (start,
    479                 (unsigned char) '/');
    480   if (NULL == end)
    481     return GNUNET_strdup ("missing '/' in payload");
    482 
    483   if (NULL != (ret = validate_payto_iban (payto_uri)))
    484     return ret; /* got a definitive answer */
    485   if (NULL != (ret = validate_payto_xtalerbank (payto_uri)))
    486     return ret; /* got a definitive answer */
    487 
    488   /* Insert validation calls for other bank account validation methods here! */
    489 
    490   return NULL;
    491 }
    492 
    493 
    494 char *
    495 TALER_normalized_payto_validate (const struct TALER_NormalizedPayto npayto_uri)
    496 {
    497   const char *payto_uri = npayto_uri.normalized_payto;
    498   char *ret;
    499 
    500   ret = payto_validate (payto_uri);
    501   if (NULL != ret)
    502     return ret;
    503   return NULL;
    504 }
    505 
    506 
    507 char *
    508 TALER_payto_validate (const struct TALER_FullPayto fpayto_uri)
    509 {
    510   const char *payto_uri = fpayto_uri.full_payto;
    511   char *ret;
    512 
    513   ret = payto_validate (payto_uri);
    514   if (NULL != ret)
    515     return ret;
    516   {
    517     char *target;
    518 
    519     target = payto_get_key (fpayto_uri,
    520                             "receiver-name=");
    521     if (NULL == target)
    522       return GNUNET_strdup ("'receiver-name' parameter missing");
    523     GNUNET_free (target);
    524   }
    525 
    526   return NULL;
    527 }
    528 
    529 
    530 char *
    531 TALER_payto_get_receiver_name (const struct TALER_FullPayto fpayto)
    532 {
    533   char *err;
    534 
    535   err = TALER_payto_validate (fpayto);
    536   if (NULL != err)
    537   {
    538     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    539                 "Invalid payto://-URI `%s': %s\n",
    540                 fpayto.full_payto,
    541                 err);
    542     GNUNET_free (err);
    543     return NULL;
    544   }
    545   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 normalize_payto_x_taler_bank (size_t len,
    563                               const char input[static len])
    564 {
    565   char *res = GNUNET_malloc (len + 1);
    566   unsigned int sc = 0;
    567 
    568   for (unsigned int i = 0; i<len; i++)
    569   {
    570     char c = input[i];
    571 
    572     if ('/' == c)
    573       sc++;
    574     if (sc < 4)
    575       res[i] = (char) tolower ((int) c);
    576     else
    577       res[i] = c;
    578   }
    579   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 normalize_payto_iban (size_t len,
    596                       const char input[static len])
    597 {
    598   char *res;
    599   size_t pos = 0;
    600   unsigned int sc = 0;
    601   bool have_bic;
    602 
    603   for (unsigned int i = 0; i<len; i++)
    604     if ('/' == input[i])
    605       sc++;
    606   if ( (sc > 4) ||
    607        (sc < 3) )
    608   {
    609     GNUNET_break (0);
    610     return NULL;
    611   }
    612   have_bic = (4 == sc);
    613   res = GNUNET_malloc (len + 1);
    614   sc = 0;
    615   for (unsigned int i = 0; i<len; i++)
    616   {
    617     char c = input[i];
    618 
    619     if ('/' == c)
    620       sc++;
    621     switch (sc)
    622     {
    623     case 0: /* payto: */
    624     case 1: /* / */
    625     case 2: /* /iban */
    626       res[pos++] = (char) tolower ((int) c);
    627       break;
    628     case 3: /* /$BIC or /$IBAN */
    629       if (have_bic)
    630         continue;
    631       res[pos++] = (char) toupper ((int) c);
    632       break;
    633     case 4: /* /$IBAN */
    634       res[pos++] = (char) toupper ((int) c);
    635       break;
    636     }
    637   }
    638   GNUNET_assert (pos <= len);
    639   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 normalize_payto_upi (size_t len,
    655                      const char input[static len])
    656 {
    657   char *res = GNUNET_malloc (len + 1);
    658 
    659   for (unsigned int i = 0; i<len; i++)
    660   {
    661     char c = input[i];
    662 
    663     res[i] = (char) tolower ((int) c);
    664   }
    665   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 normalize_payto_bitcoin (size_t len,
    682                          const char input[static len])
    683 {
    684   char *res = GNUNET_malloc (len + 1);
    685   unsigned int sc = 0;
    686 
    687   for (unsigned int i = 0; i<len; i++)
    688   {
    689     char c = input[i];
    690 
    691     if ('/' == c)
    692       sc++;
    693     if (sc < 3)
    694       res[i] = (char) tolower ((int) c);
    695     else
    696       res[i] = c;
    697   }
    698   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 normalize_payto_ilp (size_t len,
    714                      const char input[static len])
    715 {
    716   char *res = GNUNET_malloc (len + 1);
    717 
    718   for (unsigned int i = 0; i<len; i++)
    719   {
    720     char c = input[i];
    721 
    722     res[i] = (char) tolower ((int) c);
    723   }
    724   return res;
    725 }
    726 
    727 
    728 struct TALER_NormalizedPayto
    729 TALER_payto_normalize (const struct TALER_FullPayto input)
    730 {
    731   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     err = TALER_payto_validate (input);
    742     if (NULL != err)
    743     {
    744       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    745                   "Malformed payto://-URI `%s': %s\n",
    746                   input.full_payto,
    747                   err);
    748       GNUNET_free (err);
    749       return npto;
    750     }
    751   }
    752   method = TALER_payto_get_method (input.full_payto);
    753   if (NULL == method)
    754   {
    755     GNUNET_break (0);
    756     return npto;
    757   }
    758   end = strchr (input.full_payto,
    759                 '?');
    760   if (NULL == end)
    761     end = &input.full_payto[strlen (input.full_payto)];
    762   if (0 == strcasecmp (method,
    763                        "x-taler-bank"))
    764     ret = normalize_payto_x_taler_bank (end - input.full_payto,
    765                                         input.full_payto);
    766   else if (0 == strcasecmp (method,
    767                             "iban"))
    768     ret = normalize_payto_iban (end - input.full_payto,
    769                                 input.full_payto);
    770   else if (0 == strcasecmp (method,
    771                             "upi"))
    772     ret = normalize_payto_upi (end - input.full_payto,
    773                                input.full_payto);
    774   else if (0 == strcasecmp (method,
    775                             "bitcoin"))
    776     ret = normalize_payto_bitcoin (end - input.full_payto,
    777                                    input.full_payto);
    778   else if (0 == strcasecmp (method,
    779                             "ilp"))
    780     ret = normalize_payto_ilp (end - input.full_payto,
    781                                input.full_payto);
    782   else
    783     ret = GNUNET_strndup (input.full_payto,
    784                           end - input.full_payto);
    785   GNUNET_free (method);
    786   npto.normalized_payto = ret;
    787   return npto;
    788 }
    789 
    790 
    791 void
    792 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   GNUNET_CRYPTO_hash (npayto.normalized_payto,
    799                       strlen (npayto.normalized_payto) + 1,
    800                       &sha512);
    801   /* truncate */
    802   GNUNET_memcpy (h_npayto,
    803                  &sha512,
    804                  sizeof (*h_npayto));
    805   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 }
    810 
    811 
    812 void
    813 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   GNUNET_CRYPTO_hash (fpayto.full_payto,
    820                       strlen (fpayto.full_payto) + 1,
    821                       &sha512);
    822   /* truncate */
    823   GNUNET_memcpy (h_fpayto,
    824                  &sha512,
    825                  sizeof (*h_fpayto));
    826   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 }
    831 
    832 
    833 struct TALER_NormalizedPayto
    834 TALER_reserve_make_payto (const char *exchange_url,
    835                           const struct TALER_ReservePublicKeyP *reserve_pub)
    836 {
    837   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   end = GNUNET_STRINGS_data_to_string (
    846     reserve_pub,
    847     sizeof (*reserve_pub),
    848     pub_str,
    849     sizeof (pub_str));
    850   *end = '\0';
    851   if (0 == strncmp (exchange_url,
    852                     "http://",
    853                     strlen ("http://")))
    854   {
    855     is_http = true;
    856     exchange_url = &exchange_url[strlen ("http://")];
    857   }
    858   else if (0 == strncmp (exchange_url,
    859                          "https://",
    860                          strlen ("https://")))
    861   {
    862     is_http = false;
    863     exchange_url = &exchange_url[strlen ("https://")];
    864   }
    865   else
    866   {
    867     GNUNET_break (0);
    868     return npto;
    869   }
    870   /* exchange_url includes trailing '/' */
    871   GNUNET_asprintf (&reserve_url,
    872                    "payto://%s/%s%s",
    873                    is_http ? "taler-reserve-http" : "taler-reserve",
    874                    exchange_url,
    875                    pub_str);
    876   npto.normalized_payto = reserve_url;
    877   return npto;
    878 }
    879 
    880 
    881 /* end of payto.c */