Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014--2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser 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 taler-merchant-httpd_helper.c
18 : * @brief shared logic for various handlers
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include <taler/taler_util.h>
24 : #include <taler/taler_json_lib.h>
25 : #include "taler-merchant-httpd_helper.h"
26 :
27 : /**
28 : * check @a payto_uris for well-formedness
29 : *
30 : * @param payto_uris JSON array of payto URIs (presumably)
31 : * @return true if they are all valid URIs (and this is an array of strings)
32 : */
33 : bool
34 0 : TMH_payto_uri_array_valid (const json_t *payto_uris)
35 : {
36 0 : bool payto_ok = true;
37 :
38 0 : if (! json_is_array (payto_uris))
39 : {
40 0 : GNUNET_break_op (0);
41 0 : payto_ok = false;
42 : }
43 : else
44 : {
45 0 : unsigned int len = json_array_size (payto_uris);
46 :
47 0 : for (unsigned int i = 0; i<len; i++)
48 : {
49 0 : json_t *payto_uri = json_array_get (payto_uris,
50 : i);
51 : const char *uri;
52 :
53 0 : if (! json_is_string (payto_uri))
54 0 : payto_ok = false;
55 0 : uri = json_string_value (payto_uri);
56 : /* Test for the same payto:// URI being given twice */
57 0 : for (unsigned int j = 0; j<i; j++)
58 : {
59 0 : json_t *old_uri = json_array_get (payto_uris,
60 : j);
61 0 : if (json_equal (payto_uri,
62 : old_uri))
63 : {
64 0 : GNUNET_break_op (0);
65 0 : payto_ok = false;
66 0 : break;
67 : }
68 : }
69 0 : if (! payto_ok)
70 0 : break;
71 : {
72 : char *err;
73 :
74 0 : if (NULL !=
75 0 : (err = TALER_payto_validate (uri)))
76 : {
77 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
78 : "Encountered invalid payto://-URI `%s': %s\n",
79 : uri,
80 : err);
81 0 : GNUNET_free (err);
82 0 : payto_ok = false;
83 0 : break;
84 : }
85 : }
86 : }
87 : }
88 0 : return payto_ok;
89 : }
90 :
91 :
92 : bool
93 0 : TMH_location_object_valid (const json_t *location)
94 : {
95 0 : const char *country = NULL;
96 0 : const char *subdivision = NULL;
97 0 : const char *district = NULL;
98 0 : const char *town = NULL;
99 0 : const char *town_loc = NULL;
100 0 : const char *postcode = NULL;
101 0 : const char *street = NULL;
102 0 : const char *building = NULL;
103 0 : const char *building_no = NULL;
104 0 : json_t *lines = NULL;
105 : struct GNUNET_JSON_Specification spec[] = {
106 0 : GNUNET_JSON_spec_mark_optional (
107 : GNUNET_JSON_spec_string ("country",
108 : &country),
109 : NULL),
110 0 : GNUNET_JSON_spec_mark_optional (
111 : GNUNET_JSON_spec_string ("country_subdivision",
112 : &subdivision),
113 : NULL),
114 0 : GNUNET_JSON_spec_mark_optional (
115 : GNUNET_JSON_spec_string ("district",
116 : &district),
117 : NULL),
118 0 : GNUNET_JSON_spec_mark_optional (
119 : GNUNET_JSON_spec_string ("town",
120 : &town),
121 : NULL),
122 0 : GNUNET_JSON_spec_mark_optional (
123 : GNUNET_JSON_spec_string ("town_location",
124 : &town_loc),
125 : NULL),
126 0 : GNUNET_JSON_spec_mark_optional (
127 : GNUNET_JSON_spec_string ("post_code",
128 : &postcode),
129 : NULL),
130 0 : GNUNET_JSON_spec_mark_optional (
131 : GNUNET_JSON_spec_string ("street",
132 : &street),
133 : NULL),
134 0 : GNUNET_JSON_spec_mark_optional (
135 : GNUNET_JSON_spec_string ("building_name",
136 : &building),
137 : NULL),
138 0 : GNUNET_JSON_spec_mark_optional (
139 : GNUNET_JSON_spec_string ("building_number",
140 : &building_no),
141 : NULL),
142 0 : GNUNET_JSON_spec_mark_optional (
143 : GNUNET_JSON_spec_json ("address_lines",
144 : &lines),
145 : NULL),
146 0 : GNUNET_JSON_spec_end ()
147 : };
148 : const char *ename;
149 : unsigned int eline;
150 :
151 0 : if (GNUNET_OK !=
152 0 : GNUNET_JSON_parse (location,
153 : spec,
154 : &ename,
155 : &eline))
156 : {
157 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
158 : "Invalid location for field %s\n",
159 : ename);
160 0 : return false;
161 : }
162 0 : if (NULL != lines)
163 : {
164 : size_t idx;
165 : json_t *line;
166 :
167 0 : if (! json_is_array (lines))
168 : {
169 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
170 : "Invalid location for field %s\n",
171 : "lines");
172 0 : return false;
173 : }
174 0 : json_array_foreach (lines, idx, line)
175 : {
176 0 : if (! json_is_string (line))
177 : {
178 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
179 : "Invalid address line #%u in location\n",
180 : (unsigned int) idx);
181 0 : return false;
182 : }
183 : }
184 : }
185 0 : return true;
186 : }
187 :
188 :
189 : bool
190 0 : TMH_products_array_valid (const json_t *products)
191 : {
192 : const json_t *product;
193 : size_t idx;
194 0 : bool valid = true;
195 :
196 0 : if (! json_is_array (products))
197 : {
198 0 : GNUNET_break_op (0);
199 0 : return false;
200 : }
201 0 : json_array_foreach ((json_t *) products, idx, product)
202 : {
203 0 : const char *product_id = NULL;
204 : const char *description;
205 0 : json_t *description_i18n = NULL;
206 0 : uint64_t quantity = 0;
207 0 : const char *unit = NULL;
208 0 : struct TALER_Amount price = { .value = 0 };
209 0 : const char *image_data_url = NULL;
210 0 : json_t *taxes = NULL;
211 0 : struct GNUNET_TIME_Timestamp delivery_date = { 0 };
212 : struct GNUNET_JSON_Specification spec[] = {
213 0 : GNUNET_JSON_spec_mark_optional (
214 : GNUNET_JSON_spec_string ("product_id",
215 : &product_id),
216 : NULL),
217 0 : GNUNET_JSON_spec_string ("description",
218 : &description),
219 0 : GNUNET_JSON_spec_mark_optional (
220 : GNUNET_JSON_spec_json ("description_i18n",
221 : &description_i18n),
222 : NULL),
223 0 : GNUNET_JSON_spec_mark_optional (
224 : GNUNET_JSON_spec_uint64 ("quantity",
225 : &quantity),
226 : NULL),
227 0 : GNUNET_JSON_spec_mark_optional (
228 : GNUNET_JSON_spec_string ("unit",
229 : &unit),
230 : NULL),
231 0 : GNUNET_JSON_spec_mark_optional (
232 : TALER_JSON_spec_amount ("price",
233 : TMH_currency,
234 : &price),
235 : NULL),
236 0 : GNUNET_JSON_spec_mark_optional (
237 : GNUNET_JSON_spec_string ("image",
238 : &image_data_url),
239 : NULL),
240 0 : GNUNET_JSON_spec_mark_optional (
241 : GNUNET_JSON_spec_json ("taxes",
242 : &taxes),
243 : NULL),
244 0 : GNUNET_JSON_spec_mark_optional (
245 : GNUNET_JSON_spec_timestamp ("delivery_date",
246 : &delivery_date),
247 : NULL),
248 0 : GNUNET_JSON_spec_end ()
249 : };
250 : const char *ename;
251 : unsigned int eline;
252 :
253 0 : if (GNUNET_OK !=
254 0 : GNUNET_JSON_parse (product,
255 : spec,
256 : &ename,
257 : &eline))
258 : {
259 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
260 : "Invalid product #%u for field %s\n",
261 : (unsigned int) idx,
262 : ename);
263 0 : return false;
264 : }
265 0 : if ( (NULL != image_data_url) &&
266 0 : (! TMH_image_data_url_valid (image_data_url)) )
267 : {
268 0 : GNUNET_break_op (0);
269 0 : valid = false;
270 : }
271 0 : if ( (NULL != taxes) &&
272 0 : (! TMH_taxes_array_valid (taxes)) )
273 : {
274 0 : GNUNET_break_op (0);
275 0 : valid = false;
276 : }
277 0 : if ( (NULL != description_i18n) &&
278 0 : (! TALER_JSON_check_i18n (description_i18n)) )
279 : {
280 0 : GNUNET_break_op (0);
281 0 : valid = false;
282 : }
283 0 : GNUNET_JSON_parse_free (spec);
284 0 : if (! valid)
285 0 : break;
286 : }
287 :
288 0 : return valid;
289 : }
290 :
291 :
292 : bool
293 0 : TMH_image_data_url_valid (const char *image_data_url)
294 : {
295 0 : if (0 == strcmp (image_data_url,
296 : ""))
297 0 : return true;
298 0 : if (0 != strncasecmp ("data:image/",
299 : image_data_url,
300 : strlen ("data:image/")))
301 : {
302 0 : GNUNET_break_op (0);
303 0 : return false;
304 : }
305 0 : if (NULL == strstr (image_data_url,
306 : ";base64,"))
307 : {
308 0 : GNUNET_break_op (0);
309 0 : return false;
310 : }
311 0 : if (! TALER_url_valid_charset (image_data_url))
312 : {
313 0 : GNUNET_break_op (0);
314 0 : return false;
315 : }
316 0 : return true;
317 : }
318 :
319 :
320 : bool
321 0 : TMH_taxes_array_valid (const json_t *taxes)
322 : {
323 : json_t *tax;
324 : size_t idx;
325 :
326 0 : if (! json_is_array (taxes))
327 0 : return false;
328 0 : json_array_foreach (taxes, idx, tax)
329 : {
330 : struct TALER_Amount amount;
331 : const char *name;
332 : struct GNUNET_JSON_Specification spec[] = {
333 0 : GNUNET_JSON_spec_string ("name",
334 : &name),
335 0 : TALER_JSON_spec_amount_any ("tax",
336 : &amount),
337 0 : GNUNET_JSON_spec_end ()
338 : };
339 : enum GNUNET_GenericReturnValue res;
340 :
341 0 : res = TALER_MHD_parse_json_data (NULL,
342 : tax,
343 : spec);
344 0 : if (GNUNET_OK != res)
345 : {
346 0 : GNUNET_break_op (0);
347 0 : return false;
348 : }
349 : }
350 0 : return true;
351 : }
352 :
353 :
354 : struct TMH_WireMethod *
355 0 : TMH_setup_wire_account (const char *payto_uri)
356 : {
357 : struct TMH_WireMethod *wm;
358 : char *emsg;
359 :
360 0 : if (NULL != (emsg = TALER_payto_validate (payto_uri)))
361 : {
362 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
363 : "Invalid URI `%s': %s\n",
364 : payto_uri,
365 : emsg);
366 0 : GNUNET_free (emsg);
367 0 : return NULL;
368 : }
369 :
370 0 : wm = GNUNET_new (struct TMH_WireMethod);
371 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
372 0 : &wm->wire_salt,
373 : sizeof (wm->wire_salt));
374 0 : wm->payto_uri = GNUNET_strdup (payto_uri);
375 0 : TALER_merchant_wire_signature_hash (payto_uri,
376 0 : &wm->wire_salt,
377 : &wm->h_wire);
378 : wm->wire_method
379 0 : = TALER_payto_get_method (payto_uri);
380 0 : wm->active = true;
381 0 : return wm;
382 : }
383 :
384 :
385 : enum GNUNET_GenericReturnValue
386 0 : TMH_check_auth_config (struct MHD_Connection *connection,
387 : const json_t *jauth,
388 : const char **auth_token)
389 : {
390 0 : bool auth_wellformed = false;
391 0 : const char *auth_method = json_string_value (json_object_get (jauth,
392 : "method"));
393 :
394 0 : *auth_token = NULL;
395 0 : if (NULL == auth_method)
396 : {
397 0 : GNUNET_break_op (0);
398 : }
399 0 : else if (0 == strcmp (auth_method,
400 : "external"))
401 : {
402 0 : auth_wellformed = true;
403 : }
404 0 : else if (0 == strcmp (auth_method,
405 : "token"))
406 : {
407 0 : *auth_token = json_string_value (json_object_get (jauth,
408 : "token"));
409 0 : if (NULL == *auth_token)
410 : {
411 0 : GNUNET_break_op (0);
412 : }
413 : else
414 : {
415 0 : if (0 != strncasecmp (RFC_8959_PREFIX,
416 : *auth_token,
417 : strlen (RFC_8959_PREFIX)))
418 0 : GNUNET_break_op (0);
419 : else
420 0 : auth_wellformed = true;
421 : }
422 : }
423 :
424 0 : if (! auth_wellformed)
425 : {
426 0 : GNUNET_break_op (0);
427 : return (MHD_YES ==
428 0 : TALER_MHD_reply_with_error (connection,
429 : MHD_HTTP_BAD_REQUEST,
430 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
431 : "bad authentication config")) ?
432 0 : GNUNET_NO : GNUNET_SYSERR;
433 : }
434 0 : return GNUNET_OK;
435 : }
436 :
437 :
438 : /**
439 : * Generate binary UUID from client-provided UUID-string.
440 : *
441 : * @param uuids string intpu
442 : * @param[out] uuid set to binary UUID
443 : */
444 : void
445 0 : TMH_uuid_from_string (const char *uuids,
446 : struct GNUNET_Uuid *uuid)
447 : {
448 : struct GNUNET_HashCode hc;
449 :
450 0 : GNUNET_CRYPTO_hash (uuids,
451 : strlen (uuids),
452 : &hc);
453 : GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
454 0 : memcpy (uuid,
455 : &hc,
456 : sizeof (*uuid));
457 0 : }
|