diff options
Diffstat (limited to 'src/util/payto.c')
-rw-r--r-- | src/util/payto.c | 388 |
1 files changed, 343 insertions, 45 deletions
diff --git a/src/util/payto.c b/src/util/payto.c index 83a1a7e1d..5e0391883 100644 --- a/src/util/payto.c +++ b/src/util/payto.c @@ -155,7 +155,6 @@ validate_payto_iban (const char *account_url) IBAN_PREFIX, strlen (IBAN_PREFIX))) return NULL; /* not an IBAN */ - iban = strrchr (account_url, '/') + 1; #undef IBAN_PREFIX q = strchr (iban, @@ -189,6 +188,106 @@ validate_payto_iban (const char *account_url) } +/** + * Validate payto://x-taler-bank/ account URL (only account information, + * wire subject and amount are ignored). + * + * @param account_url payto URL to parse + * @return NULL on success, otherwise an error message + * to be freed by the caller + */ +static char * +validate_payto_xtalerbank (const char *account_url) +{ + const char *user; + const char *host; + bool dot_ok; + bool post_colon; + +#define XTALERBANK_PREFIX "payto://x-taler-bank/" + if (0 != strncasecmp (account_url, + XTALERBANK_PREFIX, + strlen (XTALERBANK_PREFIX))) + return NULL; /* not an IBAN */ + host = &account_url[strlen (XTALERBANK_PREFIX)]; +#undef XTALERBANK_PREFIX + user = strchr (host, '/'); + if (NULL == user) + { + return GNUNET_strdup ("account name missing"); + } + if (user == host) + { + return GNUNET_strdup ("domain name missing"); + } + if ('-' == host[0]) + return GNUNET_strdup ("invalid character '-' at start of domain name"); + if (NULL != strchr (user + 1, '/')) + return GNUNET_strdup ("invalid character '/' after account name"); + dot_ok = false; + post_colon = false; + while (host != user) + { + char c = host[0]; + + if (':' == c) + { + post_colon = true; + host++; + continue; + } + if (post_colon) + { + if (! ( ('0' <= c) && ('9' >= c) ) ) + { + char *err; + + GNUNET_asprintf (&err, + "invalid character '%c' in port", + c); + return err; + } + } + else + { + if ('.' == c) + { + if (! dot_ok) + return GNUNET_strdup ("invalid domain name (misplaced '.')"); + dot_ok = false; + } + else + { + if (! ( ('-' == c) || + ( ('0' <= c) && ('9' >= c) ) || + ( ('a' <= c) && ('z' >= c) ) || + ( ('A' <= c) && ('Z' >= c) ) ) ) + { + char *err; + + GNUNET_asprintf (&err, + "invalid character '%c' in domain name", + c); + return err; + } + dot_ok = true; + } + } + host++; + } + { + char *target; + + target = payto_get_key (account_url, + "receiver-name="); + if (NULL == target) + return GNUNET_strdup ("'receiver-name' parameter missing"); + GNUNET_free (target); + } + return NULL; +} + + char * TALER_payto_validate (const char *payto_uri) { @@ -205,7 +304,7 @@ TALER_payto_validate (const char *payto_uri) /* This is more strict than RFC 8905, alas we do not need to support messages/instructions/etc., and it is generally better to start with a narrow whitelist; we can be more permissive later ...*/ #define ALLOWED_CHARACTERS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:&?-.,=+" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:&?-.,=+%" if (NULL == strchr (ALLOWED_CHARACTERS, (int) payto_uri[i])) { @@ -229,6 +328,8 @@ TALER_payto_validate (const char *payto_uri) if (NULL != (ret = validate_payto_iban (payto_uri))) return ret; /* got a definitive answer */ + if (NULL != (ret = validate_payto_xtalerbank (payto_uri))) + return ret; /* got a definitive answer */ /* Insert other bank account validation methods here later! */ @@ -256,6 +357,242 @@ TALER_payto_get_receiver_name (const char *payto) } +/** + * Normalize "payto://x-taler-bank/$HOSTNAME/$USERNAME" + * URI in @a input. + * + * Converts to lower-case, except for $USERNAME which + * is case-sensitive. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_x_taler_bank (size_t len, + const char input[static len]) +{ + char *res = GNUNET_malloc (len + 1); + unsigned int sc = 0; + + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + if ('/' == c) + sc++; + if (sc < 4) + res[i] = (char) tolower ((int) c); + else + res[i] = c; + } + return res; +} + + +/** + * Normalize "payto://iban[/$BIC]/$IBAN" + * URI in @a input. + * + * Removes $BIC (if present) and converts $IBAN to upper-case and prefix to + * lower-case. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_iban (size_t len, + const char input[static len]) +{ + char *res; + size_t pos = 0; + unsigned int sc = 0; + bool have_bic; + + for (unsigned int i = 0; i<len; i++) + if ('/' == input[i]) + sc++; + if ( (sc > 4) || + (sc < 3) ) + { + GNUNET_break (0); + return NULL; + } + have_bic = (4 == sc); + res = GNUNET_malloc (len + 1); + sc = 0; + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + if ('/' == c) + sc++; + switch (sc) + { + case 0: /* payto: */ + case 1: /* / */ + case 2: /* /iban */ + res[pos++] = (char) tolower ((int) c); + break; + case 3: /* /$BIC or /$IBAN */ + if (have_bic) + continue; + res[pos++] = (char) toupper ((int) c); + break; + case 4: /* /$IBAN */ + res[pos++] = (char) toupper ((int) c); + break; + } + } + GNUNET_assert (pos <= len); + return res; +} + + +/** + * Normalize "payto://upi/$EMAIL" + * URI in @a input. + * + * Converts to lower-case. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_upi (size_t len, + const char input[static len]) +{ + char *res = GNUNET_malloc (len + 1); + + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + res[i] = (char) tolower ((int) c); + } + return res; +} + + +/** + * Normalize "payto://bitcoin/$ADDRESS" + * URI in @a input. + * + * Converts to lower-case, except for $ADDRESS which + * is case-sensitive. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_bitcoin (size_t len, + const char input[static len]) +{ + char *res = GNUNET_malloc (len + 1); + unsigned int sc = 0; + + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + if ('/' == c) + sc++; + if (sc < 3) + res[i] = (char) tolower ((int) c); + else + res[i] = c; + } + return res; +} + + +/** + * Normalize "payto://ilp/$NAME" + * URI in @a input. + * + * Converts to lower-case. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_ilp (size_t len, + const char input[static len]) +{ + char *res = GNUNET_malloc (len + 1); + + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + res[i] = (char) tolower ((int) c); + } + return res; +} + + +char * +TALER_payto_normalize (const char *input) +{ + char *method; + const char *end; + char *ret; + + { + char *err; + + err = TALER_payto_validate (input); + if (NULL != err) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Malformed payto://-URI `%s': %s\n", + input, + err); + GNUNET_free (err); + return NULL; + } + } + method = TALER_payto_get_method (input); + if (NULL == method) + { + GNUNET_break (0); + return NULL; + } + end = strchr (input, '?'); + if (NULL == end) + end = &input[strlen (input)]; + if (0 == strcasecmp (method, + "x-taler-bank")) + ret = normalize_payto_x_taler_bank (end - input, + input); + else if (0 == strcasecmp (method, + "iban")) + ret = normalize_payto_iban (end - input, + input); + else if (0 == strcasecmp (method, + "upi")) + ret = normalize_payto_upi (end - input, + input); + else if (0 == strcasecmp (method, + "bitcoin")) + ret = normalize_payto_bitcoin (end - input, + input); + else if (0 == strcasecmp (method, + "ilp")) + ret = normalize_payto_ilp (end - input, + input); + else + ret = GNUNET_strndup (input, + end - input); + GNUNET_free (method); + return ret; +} + + void TALER_payto_hash (const char *payto, struct TALER_PaytoHashP *h_payto) @@ -267,48 +604,9 @@ TALER_payto_hash (const char *payto, &sha512); GNUNET_static_assert (sizeof (sha512) > sizeof (*h_payto)); /* truncate */ - memcpy (h_payto, - &sha512, - sizeof (*h_payto)); -} - - -char * -TALER_payto_from_reserve (const char *exchange_base_url, - const struct TALER_ReservePublicKeyP *reserve_pub) -{ - char *payto_uri; - char *rps; - unsigned int skip; - const char *extra = ""; - int url_len; - - rps = GNUNET_STRINGS_data_to_string_alloc (reserve_pub, - sizeof (*reserve_pub)); - skip = 0; - if (0 == strncasecmp (exchange_base_url, - "http://", - strlen ("http://"))) - { - skip = strlen ("http://"); - extra = "+http"; - } - if (0 == strncasecmp (exchange_base_url, - "https://", - strlen ("https://"))) - skip = strlen ("https://"); - url_len = strlen (exchange_base_url); - if ('/' == exchange_base_url[url_len - 1]) - url_len--; - url_len -= skip; - GNUNET_asprintf (&payto_uri, - "taler%s://reserve/%.*s/%s", - extra, - url_len, - exchange_base_url + skip, - rps); - GNUNET_free (rps); - return payto_uri; + GNUNET_memcpy (h_payto, + &sha512, + sizeof (*h_payto)); } @@ -349,7 +647,7 @@ TALER_reserve_make_payto (const char *exchange_url, /* exchange_url includes trailing '/' */ GNUNET_asprintf (&reserve_url, "payto://%s/%s%s", - is_http ? "taler+http" : "taler", + is_http ? "taler-reserve-http" : "taler-reserve", exchange_url, pub_str); return reserve_url; |