Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2019-2022 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 : /**
32 : * Extract the value under @a key from the URI parameters.
33 : *
34 : * @param payto_uri the URL to parse
35 : * @param search_key key to look for, including "="
36 : * @return NULL if the @a key parameter is not found.
37 : * The caller should free the returned value.
38 : */
39 : static char *
40 5 : payto_get_key (const char *payto_uri,
41 : const char *search_key)
42 : {
43 : const char *key;
44 : const char *value_start;
45 : const char *value_end;
46 :
47 5 : key = strchr (payto_uri,
48 : (unsigned char) '?');
49 5 : if (NULL == key)
50 1 : return NULL;
51 :
52 : do {
53 4 : if (0 == strncasecmp (++key,
54 : search_key,
55 : strlen (search_key)))
56 : {
57 4 : value_start = strchr (key,
58 : (unsigned char) '=');
59 4 : if (NULL == value_start)
60 0 : return NULL;
61 4 : value_end = strchrnul (value_start,
62 : (unsigned char) '&');
63 :
64 4 : return GNUNET_strndup (value_start + 1,
65 : value_end - value_start - 1);
66 : }
67 0 : } while ( (key = strchr (key,
68 0 : (unsigned char) '&')) );
69 0 : return NULL;
70 : }
71 :
72 :
73 : char *
74 2 : TALER_payto_get_subject (const char *payto_uri)
75 : {
76 2 : return payto_get_key (payto_uri,
77 : "subject=");
78 : }
79 :
80 :
81 : char *
82 0 : TALER_payto_get_method (const char *payto_uri)
83 : {
84 : const char *start;
85 : const char *end;
86 :
87 0 : if (0 != strncasecmp (payto_uri,
88 : PAYTO,
89 : strlen (PAYTO)))
90 0 : return NULL;
91 0 : start = &payto_uri[strlen (PAYTO)];
92 0 : end = strchr (start,
93 : (unsigned char) '/');
94 0 : if (NULL == end)
95 0 : return NULL;
96 0 : return GNUNET_strndup (start,
97 : end - start);
98 : }
99 :
100 :
101 : char *
102 25 : TALER_xtalerbank_account_from_payto (const char *payto)
103 : {
104 : const char *beg;
105 : const char *end;
106 :
107 25 : if (0 != strncasecmp (payto,
108 : PAYTO "x-taler-bank/",
109 : strlen (PAYTO "x-taler-bank/")))
110 : {
111 0 : GNUNET_break_op (0);
112 0 : return NULL;
113 : }
114 25 : beg = strchr (&payto[strlen (PAYTO "x-taler-bank/")],
115 : '/');
116 25 : if (NULL == beg)
117 : {
118 0 : GNUNET_break_op (0);
119 0 : return NULL;
120 : }
121 25 : beg++; /* now points to $ACCOUNT */
122 25 : end = strchr (beg,
123 : '?');
124 25 : if (NULL == end)
125 : {
126 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
127 : "Invalid payto URI `%s'\n",
128 : payto);
129 1 : GNUNET_break_op (0);
130 1 : return GNUNET_strdup (beg); /* optional part is missing */
131 : }
132 24 : return GNUNET_strndup (beg,
133 : end - beg);
134 : }
135 :
136 :
137 : /**
138 : * Validate payto://iban/ account URL (only account information,
139 : * wire subject and amount are ignored).
140 : *
141 : * @param account_url payto URL to parse
142 : * @return NULL on success, otherwise an error message
143 : * to be freed by the caller
144 : */
145 : static char *
146 6 : validate_payto_iban (const char *account_url)
147 : {
148 : const char *iban;
149 : const char *q;
150 : char *result;
151 : char *err;
152 :
153 : #define IBAN_PREFIX "payto://iban/"
154 6 : if (0 != strncasecmp (account_url,
155 : IBAN_PREFIX,
156 : strlen (IBAN_PREFIX)))
157 3 : return NULL; /* not an IBAN */
158 :
159 3 : iban = strrchr (account_url, '/') + 1;
160 : #undef IBAN_PREFIX
161 3 : q = strchr (iban,
162 : '?');
163 3 : if (NULL != q)
164 : {
165 3 : result = GNUNET_strndup (iban,
166 : q - iban);
167 : }
168 : else
169 : {
170 0 : result = GNUNET_strdup (iban);
171 : }
172 3 : if (NULL !=
173 3 : (err = TALER_iban_validate (result)))
174 : {
175 0 : GNUNET_free (result);
176 0 : return err;
177 : }
178 3 : GNUNET_free (result);
179 : {
180 : char *target;
181 :
182 3 : target = payto_get_key (account_url,
183 : "receiver-name=");
184 3 : if (NULL == target)
185 0 : return GNUNET_strdup ("'receiver-name' parameter missing");
186 3 : GNUNET_free (target);
187 : }
188 3 : return NULL;
189 : }
190 :
191 :
192 : char *
193 6 : TALER_payto_validate (const char *payto_uri)
194 : {
195 : char *ret;
196 : const char *start;
197 : const char *end;
198 :
199 6 : if (0 != strncasecmp (payto_uri,
200 : PAYTO,
201 : strlen (PAYTO)))
202 0 : return GNUNET_strdup ("invalid prefix");
203 291 : for (unsigned int i = 0; '\0' != payto_uri[i]; i++)
204 : {
205 : /* This is more strict than RFC 8905, alas we do not need to support messages/instructions/etc.,
206 : and it is generally better to start with a narrow whitelist; we can be more permissive later ...*/
207 : #define ALLOWED_CHARACTERS \
208 : "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:&?-.,=+"
209 285 : if (NULL == strchr (ALLOWED_CHARACTERS,
210 285 : (int) payto_uri[i]))
211 : {
212 : char *ret;
213 :
214 0 : GNUNET_asprintf (&ret,
215 : "Encountered invalid character `%c' at offset %u in payto URI `%s'",
216 0 : payto_uri[i],
217 : i,
218 : payto_uri);
219 0 : return ret;
220 : }
221 : #undef ALLOWED_CHARACTERS
222 : }
223 :
224 6 : start = &payto_uri[strlen (PAYTO)];
225 6 : end = strchr (start,
226 : (unsigned char) '/');
227 6 : if (NULL == end)
228 0 : return GNUNET_strdup ("missing '/' in payload");
229 :
230 6 : if (NULL != (ret = validate_payto_iban (payto_uri)))
231 0 : return ret; /* got a definitive answer */
232 :
233 : /* Insert other bank account validation methods here later! */
234 :
235 6 : return NULL;
236 : }
237 :
238 :
239 : char *
240 0 : TALER_payto_get_receiver_name (const char *payto)
241 : {
242 : char *err;
243 :
244 0 : err = TALER_payto_validate (payto);
245 0 : if (NULL != err)
246 : {
247 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
248 : "Invalid payto://-URI `%s': %s\n",
249 : payto,
250 : err);
251 0 : GNUNET_free (err);
252 0 : return NULL;
253 : }
254 0 : return payto_get_key (payto,
255 : "receiver-name=");
256 : }
257 :
258 :
259 : void
260 7 : TALER_payto_hash (const char *payto,
261 : struct TALER_PaytoHashP *h_payto)
262 : {
263 : struct GNUNET_HashCode sha512;
264 :
265 7 : GNUNET_CRYPTO_hash (payto,
266 7 : strlen (payto) + 1,
267 : &sha512);
268 : GNUNET_static_assert (sizeof (sha512) > sizeof (*h_payto));
269 : /* truncate */
270 7 : memcpy (h_payto,
271 : &sha512,
272 : sizeof (*h_payto));
273 7 : }
274 :
275 :
276 : char *
277 0 : TALER_reserve_make_payto (const char *exchange_url,
278 : const struct TALER_ReservePublicKeyP *reserve_pub)
279 : {
280 : char pub_str[sizeof (*reserve_pub) * 2];
281 : char *end;
282 : bool is_http;
283 : char *reserve_url;
284 :
285 0 : end = GNUNET_STRINGS_data_to_string (
286 : reserve_pub,
287 : sizeof (*reserve_pub),
288 : pub_str,
289 : sizeof (pub_str));
290 0 : *end = '\0';
291 0 : if (0 == strncmp (exchange_url,
292 : "http://",
293 : strlen ("http://")))
294 : {
295 0 : is_http = true;
296 0 : exchange_url = &exchange_url[strlen ("http://")];
297 : }
298 0 : else if (0 == strncmp (exchange_url,
299 : "https://",
300 : strlen ("https://")))
301 : {
302 0 : is_http = false;
303 0 : exchange_url = &exchange_url[strlen ("https://")];
304 : }
305 : else
306 : {
307 0 : GNUNET_break (0);
308 0 : return NULL;
309 : }
310 : /* exchange_url includes trailing '/' */
311 0 : GNUNET_asprintf (&reserve_url,
312 : "payto://%s/%s%s",
313 : is_http ? "taler-reserve-http" : "taler-reserve",
314 : exchange_url,
315 : pub_str);
316 0 : return reserve_url;
317 : }
318 :
319 :
320 : /* end of payto.c */
|