Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014--2023 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 <gnunet/gnunet_db_lib.h>
24 : #include <taler/taler_json_lib.h>
25 : #include "taler-merchant-httpd_helper.h"
26 : #include <taler/taler_templating_lib.h>
27 : #include <taler/taler_dbevents.h>
28 :
29 :
30 : void
31 0 : TMH_quantity_defaults_from_unit (const struct TMH_MerchantInstance *mi,
32 : const char *unit,
33 : bool *allow_fractional,
34 : uint32_t *precision_level)
35 : {
36 0 : GNUNET_assert (NULL != allow_fractional);
37 0 : GNUNET_assert (NULL != precision_level);
38 0 : if (GNUNET_OK !=
39 0 : TMH_unit_defaults_for_instance (mi,
40 : unit,
41 : allow_fractional,
42 : precision_level))
43 : {
44 0 : *allow_fractional = false;
45 0 : *precision_level = 0;
46 : }
47 0 : }
48 :
49 :
50 : enum GNUNET_GenericReturnValue
51 36 : TMH_unit_defaults_for_instance (const struct TMH_MerchantInstance *mi,
52 : const char *unit,
53 : bool *allow_fractional,
54 : uint32_t *precision_level)
55 : {
56 36 : struct TALER_MERCHANTDB_UnitDetails ud = { 0 };
57 : enum GNUNET_DB_QueryStatus qs;
58 36 : bool allow = false;
59 36 : uint32_t precision = 0;
60 :
61 36 : GNUNET_assert (NULL != allow_fractional);
62 36 : GNUNET_assert (NULL != precision_level);
63 :
64 36 : qs = TMH_db->select_unit (TMH_db->cls,
65 36 : mi->settings.id,
66 : unit,
67 : &ud);
68 36 : switch (qs)
69 : {
70 0 : case GNUNET_DB_STATUS_HARD_ERROR:
71 : case GNUNET_DB_STATUS_SOFT_ERROR:
72 0 : TALER_MERCHANTDB_unit_details_free (&ud);
73 0 : return GNUNET_SYSERR;
74 2 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
75 2 : allow = ud.unit_allow_fraction;
76 2 : precision = ud.unit_precision_level;
77 2 : break;
78 34 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
79 34 : break;
80 0 : default:
81 0 : GNUNET_break (0);
82 0 : TALER_MERCHANTDB_unit_details_free (&ud);
83 0 : return GNUNET_SYSERR;
84 : }
85 36 : TALER_MERCHANTDB_unit_details_free (&ud);
86 :
87 : /* This is definitely not supposed to happen
88 : combination of allow -> false, and precision > 0
89 : yet let's fix it */
90 36 : if (! allow)
91 : {
92 34 : GNUNET_break (0);
93 34 : precision = 0;
94 : }
95 36 : *allow_fractional = allow;
96 36 : *precision_level = precision;
97 36 : return GNUNET_OK;
98 : }
99 :
100 :
101 : enum GNUNET_GenericReturnValue
102 0 : TMH_cmp_wire_account (
103 : const json_t *account,
104 : const struct TMH_WireMethod *wm)
105 : {
106 0 : const char *credit_facade_url = NULL;
107 0 : const json_t *credit_facade_credentials = NULL;
108 : struct TALER_FullPayto uri;
109 : struct GNUNET_JSON_Specification ispec[] = {
110 0 : TALER_JSON_spec_full_payto_uri ("payto_uri",
111 : &uri),
112 0 : GNUNET_JSON_spec_mark_optional (
113 : TALER_JSON_spec_web_url ("credit_facade_url",
114 : &credit_facade_url),
115 : NULL),
116 0 : GNUNET_JSON_spec_mark_optional (
117 : GNUNET_JSON_spec_object_const ("credit_facade_credentials",
118 : &credit_facade_credentials),
119 : NULL),
120 0 : GNUNET_JSON_spec_end ()
121 : };
122 : enum GNUNET_GenericReturnValue res;
123 : const char *ename;
124 : unsigned int eline;
125 :
126 0 : res = GNUNET_JSON_parse (account,
127 : ispec,
128 : &ename,
129 : &eline);
130 0 : if (GNUNET_OK != res)
131 : {
132 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
133 : "Failed to parse account spec: %s (%u)\n",
134 : ename,
135 : eline);
136 0 : return GNUNET_SYSERR;
137 : }
138 0 : if (0 !=
139 0 : TALER_full_payto_cmp (wm->payto_uri,
140 : uri))
141 : {
142 0 : return GNUNET_SYSERR;
143 : }
144 0 : if ( (NULL == credit_facade_url) !=
145 0 : (NULL == wm->credit_facade_url) ||
146 0 : (NULL == credit_facade_credentials) !=
147 0 : (NULL == wm->credit_facade_credentials) )
148 : {
149 0 : return GNUNET_NO;
150 : }
151 0 : if ( (NULL != credit_facade_url) &&
152 0 : (0 != strcmp (credit_facade_url,
153 0 : wm->credit_facade_url)) )
154 : {
155 0 : return GNUNET_NO;
156 : }
157 0 : if ( (NULL != credit_facade_credentials) &&
158 0 : (0 != json_equal (credit_facade_credentials,
159 0 : wm->credit_facade_credentials)) )
160 : {
161 0 : return GNUNET_NO;
162 : }
163 0 : return GNUNET_YES;
164 : }
165 :
166 :
167 : bool
168 0 : TMH_accounts_array_valid (const json_t *accounts)
169 : {
170 : size_t len;
171 :
172 0 : if (! json_is_array (accounts))
173 : {
174 0 : GNUNET_break_op (0);
175 0 : return false;
176 : }
177 0 : len = json_array_size (accounts);
178 0 : for (size_t i = 0; i<len; i++)
179 : {
180 0 : json_t *payto_uri = json_array_get (accounts,
181 : i);
182 0 : const char *credit_facade_url = NULL;
183 0 : const json_t *credit_facade_credentials = NULL;
184 : struct TALER_FullPayto uri;
185 : struct GNUNET_JSON_Specification ispec[] = {
186 0 : TALER_JSON_spec_full_payto_uri ("payto_uri",
187 : &uri),
188 0 : GNUNET_JSON_spec_mark_optional (
189 : TALER_JSON_spec_web_url ("credit_facade_url",
190 : &credit_facade_url),
191 : NULL),
192 0 : GNUNET_JSON_spec_mark_optional (
193 : GNUNET_JSON_spec_object_const ("credit_facade_credentials",
194 : &credit_facade_credentials),
195 : NULL),
196 0 : GNUNET_JSON_spec_end ()
197 : };
198 : enum GNUNET_GenericReturnValue res;
199 : const char *ename;
200 : unsigned int eline;
201 :
202 0 : res = GNUNET_JSON_parse (payto_uri,
203 : ispec,
204 : &ename,
205 : &eline);
206 0 : if (GNUNET_OK != res)
207 : {
208 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
209 : "Failed to parse account spec: %s (%u)\n",
210 : ename,
211 : eline);
212 0 : return false;
213 : }
214 :
215 : /* Test for the same payto:// URI being given twice */
216 0 : for (size_t j = 0; j<i; j++)
217 : {
218 0 : json_t *old_uri = json_array_get (accounts,
219 : j);
220 0 : if (0 == strcmp (uri.full_payto,
221 : json_string_value (
222 0 : json_object_get (old_uri,
223 : "payto_uri"))))
224 : {
225 0 : GNUNET_break_op (0);
226 0 : return false;
227 : }
228 : }
229 : {
230 : char *err;
231 :
232 0 : if (NULL !=
233 0 : (err = TALER_payto_validate (uri)))
234 : {
235 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
236 : "Encountered invalid payto://-URI `%s': %s\n",
237 : uri.full_payto,
238 : err);
239 0 : GNUNET_free (err);
240 0 : return false;
241 : }
242 : }
243 0 : if ( (NULL == credit_facade_url) !=
244 : (NULL == credit_facade_credentials) )
245 : {
246 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
247 : "If credit_facade_url is given, credit_facade_credentials must also be specified (violated for %s)\n",
248 : uri.full_payto);
249 0 : return false;
250 : }
251 0 : if ( (NULL != credit_facade_url) ||
252 0 : (NULL != credit_facade_credentials) )
253 : {
254 : struct TALER_MERCHANT_BANK_AuthenticationData auth;
255 :
256 0 : if (GNUNET_OK !=
257 0 : TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
258 : credit_facade_url,
259 : &auth))
260 : {
261 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
262 : "Invalid credit facade URL or credentials `%s'\n",
263 : credit_facade_url);
264 0 : return false;
265 : }
266 0 : TALER_MERCHANT_BANK_auth_free (&auth);
267 : }
268 : } /* end for all accounts */
269 0 : return true;
270 : }
271 :
272 :
273 : bool
274 110 : TMH_location_object_valid (const json_t *location)
275 : {
276 110 : const char *country = NULL;
277 110 : const char *subdivision = NULL;
278 110 : const char *district = NULL;
279 110 : const char *town = NULL;
280 110 : const char *town_loc = NULL;
281 110 : const char *postcode = NULL;
282 110 : const char *street = NULL;
283 110 : const char *building = NULL;
284 110 : const char *building_no = NULL;
285 110 : const json_t *lines = NULL;
286 : struct GNUNET_JSON_Specification spec[] = {
287 110 : GNUNET_JSON_spec_mark_optional (
288 : GNUNET_JSON_spec_string ("country",
289 : &country),
290 : NULL),
291 110 : GNUNET_JSON_spec_mark_optional (
292 : GNUNET_JSON_spec_string ("country_subdivision",
293 : &subdivision),
294 : NULL),
295 110 : GNUNET_JSON_spec_mark_optional (
296 : GNUNET_JSON_spec_string ("district",
297 : &district),
298 : NULL),
299 110 : GNUNET_JSON_spec_mark_optional (
300 : GNUNET_JSON_spec_string ("town",
301 : &town),
302 : NULL),
303 110 : GNUNET_JSON_spec_mark_optional (
304 : GNUNET_JSON_spec_string ("town_location",
305 : &town_loc),
306 : NULL),
307 110 : GNUNET_JSON_spec_mark_optional (
308 : GNUNET_JSON_spec_string ("post_code",
309 : &postcode),
310 : NULL),
311 110 : GNUNET_JSON_spec_mark_optional (
312 : GNUNET_JSON_spec_string ("street",
313 : &street),
314 : NULL),
315 110 : GNUNET_JSON_spec_mark_optional (
316 : GNUNET_JSON_spec_string ("building_name",
317 : &building),
318 : NULL),
319 110 : GNUNET_JSON_spec_mark_optional (
320 : GNUNET_JSON_spec_string ("building_number",
321 : &building_no),
322 : NULL),
323 110 : GNUNET_JSON_spec_mark_optional (
324 : GNUNET_JSON_spec_array_const ("address_lines",
325 : &lines),
326 : NULL),
327 110 : GNUNET_JSON_spec_end ()
328 : };
329 : const char *ename;
330 : unsigned int eline;
331 :
332 110 : if (GNUNET_OK !=
333 110 : GNUNET_JSON_parse (location,
334 : spec,
335 : &ename,
336 : &eline))
337 : {
338 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
339 : "Invalid location for field %s\n",
340 : ename);
341 0 : return false;
342 : }
343 110 : if (NULL != lines)
344 : {
345 : size_t idx;
346 : json_t *line;
347 :
348 0 : json_array_foreach (lines, idx, line)
349 : {
350 0 : if (! json_is_string (line))
351 : {
352 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
353 : "Invalid address line #%u in location\n",
354 : (unsigned int) idx);
355 0 : return false;
356 : }
357 : }
358 : }
359 110 : return true;
360 : }
361 :
362 :
363 : bool
364 71 : TMH_products_array_valid (const json_t *products)
365 : {
366 : const json_t *product;
367 : size_t idx;
368 71 : bool valid = true;
369 :
370 71 : if (! json_is_array (products))
371 : {
372 0 : GNUNET_break_op (0);
373 0 : return false;
374 : }
375 91 : json_array_foreach ((json_t *) products, idx, product)
376 : {
377 20 : const char *product_id = NULL;
378 : const char *description;
379 20 : uint64_t quantity = 0;
380 20 : bool quantity_missing = true;
381 20 : const char *unit_quantity = NULL;
382 20 : bool unit_quantity_missing = true;
383 20 : const char *unit = NULL;
384 20 : struct TALER_Amount price = { .value = 0 };
385 20 : const char *image_data_url = NULL;
386 20 : const json_t *taxes = NULL;
387 20 : struct GNUNET_TIME_Timestamp delivery_date = { 0 };
388 : struct GNUNET_JSON_Specification spec[] = {
389 20 : GNUNET_JSON_spec_mark_optional (
390 : GNUNET_JSON_spec_string ("product_id",
391 : &product_id),
392 : NULL),
393 20 : TALER_JSON_spec_i18n_str ("description",
394 : &description),
395 20 : GNUNET_JSON_spec_mark_optional (
396 : GNUNET_JSON_spec_uint64 ("quantity",
397 : &quantity),
398 : &quantity_missing),
399 20 : GNUNET_JSON_spec_mark_optional (
400 : GNUNET_JSON_spec_string ("unit_quantity",
401 : &unit_quantity),
402 : &unit_quantity_missing),
403 20 : GNUNET_JSON_spec_mark_optional (
404 : GNUNET_JSON_spec_string ("unit",
405 : &unit),
406 : NULL),
407 20 : GNUNET_JSON_spec_mark_optional (
408 : TALER_JSON_spec_amount_any ("price",
409 : &price),
410 : NULL),
411 20 : GNUNET_JSON_spec_mark_optional (
412 : GNUNET_JSON_spec_string ("image",
413 : &image_data_url),
414 : NULL),
415 20 : GNUNET_JSON_spec_mark_optional (
416 : GNUNET_JSON_spec_array_const ("taxes",
417 : &taxes),
418 : NULL),
419 20 : GNUNET_JSON_spec_mark_optional (
420 : GNUNET_JSON_spec_timestamp ("delivery_date",
421 : &delivery_date),
422 : NULL),
423 20 : GNUNET_JSON_spec_end ()
424 : };
425 : const char *ename;
426 : unsigned int eline;
427 :
428 20 : if (GNUNET_OK !=
429 20 : GNUNET_JSON_parse (product,
430 : spec,
431 : &ename,
432 : &eline))
433 : {
434 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
435 : "Invalid product #%u for field %s\n",
436 : (unsigned int) idx,
437 : ename);
438 0 : return false;
439 : }
440 20 : if (quantity_missing)
441 : {
442 2 : if (unit_quantity_missing)
443 : {
444 0 : GNUNET_break_op (0);
445 0 : valid = false;
446 : }
447 : }
448 20 : if ( (NULL != image_data_url) &&
449 19 : (! TALER_MERCHANT_image_data_url_valid (image_data_url)) )
450 : {
451 0 : GNUNET_break_op (0);
452 0 : valid = false;
453 : }
454 20 : if ( (NULL != taxes) &&
455 19 : (! TALER_MERCHANT_taxes_array_valid (taxes)) )
456 : {
457 0 : GNUNET_break_op (0);
458 0 : valid = false;
459 : }
460 20 : GNUNET_JSON_parse_free (spec);
461 20 : if (! valid)
462 0 : break;
463 : }
464 :
465 71 : return valid;
466 : }
467 :
468 :
469 : bool
470 16 : TMH_template_contract_valid (const json_t *template_contract)
471 : {
472 : const char *summary;
473 : const char *currency;
474 16 : struct TALER_Amount amount = { .value = 0};
475 16 : uint32_t minimum_age = 0;
476 16 : struct GNUNET_TIME_Relative pay_duration = { 0 };
477 : struct GNUNET_JSON_Specification spec[] = {
478 16 : GNUNET_JSON_spec_mark_optional (
479 : GNUNET_JSON_spec_string ("summary",
480 : &summary),
481 : NULL),
482 16 : GNUNET_JSON_spec_mark_optional (
483 : GNUNET_JSON_spec_string ("currency",
484 : ¤cy),
485 : NULL),
486 16 : GNUNET_JSON_spec_mark_optional (
487 : TALER_JSON_spec_amount_any ("amount",
488 : &amount),
489 : NULL),
490 16 : GNUNET_JSON_spec_uint32 ("minimum_age",
491 : &minimum_age),
492 16 : GNUNET_JSON_spec_relative_time ("pay_duration",
493 : &pay_duration),
494 16 : GNUNET_JSON_spec_end ()
495 : };
496 : const char *ename;
497 : unsigned int eline;
498 :
499 16 : if (GNUNET_OK !=
500 16 : GNUNET_JSON_parse (template_contract,
501 : spec,
502 : &ename,
503 : &eline))
504 : {
505 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
506 : "Invalid template_contract for field %s\n",
507 : ename);
508 0 : return false;
509 : }
510 16 : return true;
511 : }
512 :
513 :
514 : struct TMH_WireMethod *
515 21 : TMH_setup_wire_account (
516 : struct TALER_FullPayto payto_uri,
517 : const char *credit_facade_url,
518 : const json_t *credit_facade_credentials)
519 : {
520 : struct TMH_WireMethod *wm;
521 : char *emsg;
522 :
523 21 : if (NULL != (emsg = TALER_payto_validate (payto_uri)))
524 : {
525 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
526 : "Invalid URI `%s': %s\n",
527 : payto_uri.full_payto,
528 : emsg);
529 0 : GNUNET_free (emsg);
530 0 : return NULL;
531 : }
532 :
533 21 : wm = GNUNET_new (struct TMH_WireMethod);
534 21 : if (NULL != credit_facade_url)
535 : wm->credit_facade_url
536 2 : = GNUNET_strdup (credit_facade_url);
537 21 : if (NULL != credit_facade_credentials)
538 : wm->credit_facade_credentials
539 2 : = json_incref ((json_t*) credit_facade_credentials);
540 21 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
541 21 : &wm->wire_salt,
542 : sizeof (wm->wire_salt));
543 : wm->payto_uri.full_payto
544 21 : = GNUNET_strdup (payto_uri.full_payto);
545 21 : TALER_merchant_wire_signature_hash (payto_uri,
546 21 : &wm->wire_salt,
547 : &wm->h_wire);
548 : wm->wire_method
549 21 : = TALER_payto_get_method (payto_uri.full_payto);
550 21 : wm->active = true;
551 21 : return wm;
552 : }
553 :
554 :
555 : enum TALER_ErrorCode
556 49 : TMH_check_token (const char *token,
557 : const char *instance_id,
558 : enum TMH_AuthScope *as)
559 : {
560 : enum TMH_AuthScope scope;
561 : struct GNUNET_TIME_Timestamp expiration;
562 : enum GNUNET_DB_QueryStatus qs;
563 : struct TALER_MERCHANTDB_LoginTokenP btoken;
564 :
565 49 : if (NULL == token)
566 : {
567 6 : *as = TMH_AS_NONE;
568 6 : return TALER_EC_NONE;
569 : }
570 43 : if (0 != strncasecmp (token,
571 : RFC_8959_PREFIX,
572 : strlen (RFC_8959_PREFIX)))
573 : {
574 0 : *as = TMH_AS_NONE;
575 0 : return TALER_EC_NONE;
576 : }
577 43 : token += strlen (RFC_8959_PREFIX);
578 43 : if (GNUNET_OK !=
579 43 : GNUNET_STRINGS_string_to_data (token,
580 : strlen (token),
581 : &btoken,
582 : sizeof (btoken)))
583 : {
584 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
585 : "Given authorization token `%s' is malformed\n",
586 : token);
587 0 : GNUNET_break_op (0);
588 0 : return TALER_EC_GENERIC_TOKEN_MALFORMED;
589 : }
590 43 : qs = TMH_db->select_login_token (TMH_db->cls,
591 : instance_id,
592 : &btoken,
593 : &expiration,
594 : (uint32_t*) &scope);
595 43 : if (qs < 0)
596 : {
597 0 : GNUNET_break (0);
598 0 : return TALER_EC_GENERIC_DB_FETCH_FAILED;
599 : }
600 43 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
601 : {
602 9 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
603 : "Authorization token `%s' unknown\n",
604 : token);
605 9 : return TALER_EC_GENERIC_TOKEN_UNKNOWN;
606 : }
607 34 : if (GNUNET_TIME_absolute_is_past (expiration.abs_time))
608 : {
609 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610 : "Authorization token `%s' expired\n",
611 : token);
612 0 : return TALER_EC_GENERIC_TOKEN_EXPIRED;
613 : }
614 34 : *as = scope;
615 34 : return TALER_EC_NONE;
616 : }
617 :
618 :
619 : enum GNUNET_GenericReturnValue
620 42 : TMH_check_auth_config (struct MHD_Connection *connection,
621 : const json_t *jauth,
622 : const char **auth_password)
623 : {
624 42 : bool auth_wellformed = false;
625 42 : const char *auth_method = json_string_value (json_object_get (jauth,
626 : "method"));
627 :
628 42 : *auth_password = NULL;
629 42 : if (NULL == auth_method)
630 : {
631 0 : GNUNET_break_op (0);
632 : }
633 42 : else if ((GNUNET_YES != TMH_strict_v19) &&
634 42 : (0 == strcmp (auth_method,
635 : "external")))
636 : {
637 29 : auth_wellformed = true;
638 : }
639 13 : else if (GNUNET_YES == TMH_auth_disabled)
640 : {
641 0 : auth_wellformed = true;
642 : }
643 13 : else if (0 == strcmp (auth_method,
644 : "token"))
645 : {
646 : json_t *pw_value;
647 :
648 13 : pw_value = json_object_get (jauth,
649 : "password");
650 13 : if (NULL == pw_value)
651 : {
652 0 : pw_value = json_object_get (jauth,
653 : "token");
654 : }
655 13 : if (NULL == pw_value)
656 : {
657 0 : auth_wellformed = false;
658 0 : GNUNET_break_op (0);
659 : }
660 : else
661 : {
662 13 : *auth_password = json_string_value (pw_value);
663 13 : if (NULL != *auth_password)
664 : {
665 13 : if (0 == strncasecmp (RFC_8959_PREFIX,
666 : *auth_password,
667 : strlen (RFC_8959_PREFIX)))
668 : {
669 4 : *auth_password = *auth_password + strlen (RFC_8959_PREFIX);
670 : }
671 13 : auth_wellformed = true;
672 : }
673 : }
674 : }
675 :
676 42 : if (! auth_wellformed)
677 : {
678 0 : GNUNET_break_op (0);
679 : return (MHD_YES ==
680 0 : TALER_MHD_reply_with_error (connection,
681 : MHD_HTTP_BAD_REQUEST,
682 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
683 : "bad authentication config"))
684 : ? GNUNET_NO
685 0 : : GNUNET_SYSERR;
686 : }
687 42 : return GNUNET_OK;
688 : }
689 :
690 :
691 : void
692 17 : TMH_uuid_from_string (const char *uuids,
693 : struct GNUNET_Uuid *uuid)
694 : {
695 : struct GNUNET_HashCode hc;
696 :
697 17 : GNUNET_CRYPTO_hash (uuids,
698 : strlen (uuids),
699 : &hc);
700 : GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
701 17 : GNUNET_memcpy (uuid,
702 : &hc,
703 : sizeof (*uuid));
704 17 : }
705 :
706 :
707 : /**
708 : * Closure for #trigger_webhook_cb.
709 : *
710 : * @param instance which is the instance we work with
711 : * @param root JSON data to fill into the template
712 : * @param rv, query of the TALER_TEMPLATEING_fill
713 : */
714 : struct Trigger
715 : {
716 : const char *instance;
717 :
718 : const json_t *root;
719 :
720 : enum GNUNET_DB_QueryStatus rv;
721 :
722 : };
723 :
724 : /**
725 : * Typically called by `TMH_trigger_webhook`.
726 : *
727 : * @param[in,out] cls a `struct Trigger` with information about the webhook
728 : * @param webhook_serial reference to the configured webhook template.
729 : * @param event_type is the event/action of the webhook
730 : * @param url to make request to
731 : * @param http_method use for the webhook
732 : * @param header_template of the webhook
733 : * @param body_template of the webhook
734 : */
735 : static void
736 4 : trigger_webhook_cb (void *cls,
737 : uint64_t webhook_serial,
738 : const char *event_type,
739 : const char *url,
740 : const char *http_method,
741 : const char *header_template,
742 : const char *body_template)
743 : {
744 4 : struct Trigger *t = cls;
745 4 : void *header = NULL;
746 4 : void *body = NULL;
747 : size_t header_size;
748 : size_t body_size;
749 :
750 4 : if (NULL != header_template)
751 : {
752 : int ret;
753 :
754 4 : ret = TALER_TEMPLATING_fill (header_template,
755 : t->root,
756 : &header,
757 : &header_size);
758 4 : if (0 != ret)
759 : {
760 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
761 : "Failed to expand webhook header template for webhook %llu (%d)\n",
762 : (unsigned long long) webhook_serial,
763 : ret);
764 0 : t->rv = GNUNET_DB_STATUS_HARD_ERROR;
765 0 : return;
766 : }
767 : /* Note: header is actually header_size+1 bytes long here, see mustach.c::memfile_close() */
768 4 : GNUNET_assert ('\0' == ((const char *) header)[header_size]);
769 : }
770 4 : if (NULL != body_template)
771 : {
772 : int ret;
773 4 : ret = TALER_TEMPLATING_fill (body_template,
774 : t->root,
775 : &body,
776 : &body_size);
777 4 : if (0 != ret)
778 : {
779 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
780 : "Failed to expand webhook body template for webhook %llu (%d)\n",
781 : (unsigned long long) webhook_serial,
782 : ret);
783 0 : t->rv = GNUNET_DB_STATUS_HARD_ERROR;
784 0 : return;
785 : }
786 : /* Note: body is actually body_size+1 bytes long here, see mustach.c::memfile_close() */
787 4 : GNUNET_assert ('\0' == ((const char *) body)[body_size]);
788 : }
789 4 : t->rv = TMH_db->insert_pending_webhook (TMH_db->cls,
790 : t->instance,
791 : webhook_serial,
792 : url,
793 : http_method,
794 : header,
795 : body);
796 4 : if (t->rv > 0)
797 : {
798 4 : struct GNUNET_DB_EventHeaderP es = {
799 4 : .size = htons (sizeof(es)),
800 4 : .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
801 : };
802 4 : const void *extra = NULL;
803 4 : size_t extra_size = 0;
804 4 : TMH_db->event_notify (TMH_db->cls,
805 : &es,
806 : &extra,
807 : extra_size);
808 : }
809 4 : free (header);
810 4 : free (body);
811 : }
812 :
813 :
814 : /**
815 : * TMH_trigger_webhook is a function that need to be use when someone
816 : * pay. Merchant need to have a notification.
817 : *
818 : * @param instance that we need to send the webhook as a notification
819 : * @param event of the webhook
820 : * @param args argument of the function
821 : */
822 : enum GNUNET_DB_QueryStatus
823 99 : TMH_trigger_webhook (const char *instance,
824 : const char *event,
825 : const json_t *args)
826 : {
827 99 : struct Trigger t = {
828 : .instance = instance,
829 : .root = args
830 : };
831 : enum GNUNET_DB_QueryStatus qs;
832 :
833 99 : qs = TMH_db->lookup_webhook_by_event (TMH_db->cls,
834 : instance,
835 : event,
836 : &trigger_webhook_cb,
837 : &t);
838 99 : if (qs < 0)
839 0 : return qs;
840 99 : return t.rv;
841 : }
842 :
843 :
844 : enum GNUNET_GenericReturnValue
845 152 : TMH_base_url_by_connection (struct MHD_Connection *connection,
846 : const char *instance,
847 : struct GNUNET_Buffer *buf)
848 : {
849 : const char *host;
850 : const char *forwarded_host;
851 : const char *forwarded_port;
852 : const char *uri_path;
853 :
854 152 : memset (buf,
855 : 0,
856 : sizeof (*buf));
857 152 : if (NULL != TMH_base_url)
858 : {
859 0 : GNUNET_buffer_write_str (buf,
860 : TMH_base_url);
861 : }
862 : else
863 : {
864 152 : if (GNUNET_YES ==
865 152 : TALER_mhd_is_https (connection))
866 0 : GNUNET_buffer_write_str (buf,
867 : "https://");
868 : else
869 152 : GNUNET_buffer_write_str (buf,
870 : "http://");
871 152 : host = MHD_lookup_connection_value (connection,
872 : MHD_HEADER_KIND,
873 : MHD_HTTP_HEADER_HOST);
874 152 : forwarded_host = MHD_lookup_connection_value (connection,
875 : MHD_HEADER_KIND,
876 : "X-Forwarded-Host");
877 152 : if (NULL != forwarded_host)
878 : {
879 0 : GNUNET_buffer_write_str (buf,
880 : forwarded_host);
881 : }
882 : else
883 : {
884 152 : if (NULL == host)
885 : {
886 0 : GNUNET_buffer_clear (buf);
887 0 : GNUNET_break (0);
888 0 : return GNUNET_SYSERR;
889 : }
890 152 : GNUNET_buffer_write_str (buf,
891 : host);
892 : }
893 152 : forwarded_port = MHD_lookup_connection_value (connection,
894 : MHD_HEADER_KIND,
895 : "X-Forwarded-Port");
896 152 : if (NULL != forwarded_port)
897 : {
898 0 : GNUNET_buffer_write_str (buf,
899 : ":");
900 0 : GNUNET_buffer_write_str (buf,
901 : forwarded_port);
902 : }
903 152 : uri_path = MHD_lookup_connection_value (connection,
904 : MHD_HEADER_KIND,
905 : "X-Forwarded-Prefix");
906 152 : if (NULL != uri_path)
907 0 : GNUNET_buffer_write_path (buf,
908 : uri_path);
909 : }
910 152 : if (0 != strcmp (instance,
911 : "admin"))
912 : {
913 13 : GNUNET_buffer_write_path (buf,
914 : "/instances/");
915 13 : GNUNET_buffer_write_str (buf,
916 : instance);
917 : }
918 152 : return GNUNET_OK;
919 : }
920 :
921 :
922 : enum GNUNET_GenericReturnValue
923 35 : TMH_taler_uri_by_connection (struct MHD_Connection *connection,
924 : const char *method,
925 : const char *instance,
926 : struct GNUNET_Buffer *buf)
927 : {
928 : const char *host;
929 : const char *forwarded_host;
930 : const char *forwarded_port;
931 : const char *uri_path;
932 :
933 35 : memset (buf,
934 : 0,
935 : sizeof (*buf));
936 35 : host = MHD_lookup_connection_value (connection,
937 : MHD_HEADER_KIND,
938 : "Host");
939 35 : forwarded_host = MHD_lookup_connection_value (connection,
940 : MHD_HEADER_KIND,
941 : "X-Forwarded-Host");
942 35 : forwarded_port = MHD_lookup_connection_value (connection,
943 : MHD_HEADER_KIND,
944 : "X-Forwarded-Port");
945 35 : uri_path = MHD_lookup_connection_value (connection,
946 : MHD_HEADER_KIND,
947 : "X-Forwarded-Prefix");
948 35 : if (NULL != forwarded_host)
949 0 : host = forwarded_host;
950 35 : if (NULL == host)
951 : {
952 0 : GNUNET_break (0);
953 0 : return GNUNET_SYSERR;
954 : }
955 35 : GNUNET_buffer_write_str (buf,
956 : "taler");
957 35 : if (GNUNET_NO == TALER_mhd_is_https (connection))
958 35 : GNUNET_buffer_write_str (buf,
959 : "+http");
960 35 : GNUNET_buffer_write_str (buf,
961 : "://");
962 35 : GNUNET_buffer_write_str (buf,
963 : method);
964 35 : GNUNET_buffer_write_str (buf,
965 : "/");
966 35 : GNUNET_buffer_write_str (buf,
967 : host);
968 35 : if (NULL != forwarded_port)
969 : {
970 0 : GNUNET_buffer_write_str (buf,
971 : ":");
972 0 : GNUNET_buffer_write_str (buf,
973 : forwarded_port);
974 : }
975 35 : if (NULL != uri_path)
976 0 : GNUNET_buffer_write_path (buf,
977 : uri_path);
978 35 : if (0 != strcmp ("admin",
979 : instance))
980 : {
981 3 : GNUNET_buffer_write_path (buf,
982 : "instances");
983 3 : GNUNET_buffer_write_path (buf,
984 : instance);
985 : }
986 35 : return GNUNET_OK;
987 : }
988 :
989 :
990 : /**
991 : * Closure for #add_matching_account().
992 : */
993 : struct ExchangeMatchContext
994 : {
995 : /**
996 : * Wire method to match, NULL for all.
997 : */
998 : const char *wire_method;
999 :
1000 : /**
1001 : * Array of accounts to return.
1002 : */
1003 : json_t *accounts;
1004 : };
1005 :
1006 :
1007 : /**
1008 : * If the given account is feasible, add it to the array
1009 : * of accounts we return.
1010 : *
1011 : * @param cls a `struct PostReserveContext`
1012 : * @param payto_uri URI of the account
1013 : * @param conversion_url URL of a conversion service
1014 : * @param debit_restrictions restrictions for debits from account
1015 : * @param credit_restrictions restrictions for credits to account
1016 : * @param master_sig signature affirming the account
1017 : */
1018 : static void
1019 0 : add_matching_account (
1020 : void *cls,
1021 : struct TALER_FullPayto payto_uri,
1022 : const char *conversion_url,
1023 : const json_t *debit_restrictions,
1024 : const json_t *credit_restrictions,
1025 : const struct TALER_MasterSignatureP *master_sig)
1026 : {
1027 0 : struct ExchangeMatchContext *rc = cls;
1028 : char *method;
1029 :
1030 0 : method = TALER_payto_get_method (payto_uri.full_payto);
1031 0 : if ( (NULL == rc->wire_method) ||
1032 0 : (0 == strcmp (method,
1033 : rc->wire_method)) )
1034 : {
1035 : json_t *acc;
1036 :
1037 0 : acc = GNUNET_JSON_PACK (
1038 : TALER_JSON_pack_full_payto ("payto_uri",
1039 : payto_uri),
1040 : GNUNET_JSON_pack_data_auto ("master_sig",
1041 : master_sig),
1042 : GNUNET_JSON_pack_allow_null (
1043 : GNUNET_JSON_pack_string ("conversion_url",
1044 : conversion_url)),
1045 : GNUNET_JSON_pack_array_incref ("credit_restrictions",
1046 : (json_t *) credit_restrictions),
1047 : GNUNET_JSON_pack_array_incref ("debit_restrictions",
1048 : (json_t *) debit_restrictions)
1049 : );
1050 0 : GNUNET_assert (0 ==
1051 : json_array_append_new (rc->accounts,
1052 : acc));
1053 : }
1054 0 : GNUNET_free (method);
1055 0 : }
1056 :
1057 :
1058 : /**
1059 : * Return JSON array with all of the exchange accounts
1060 : * that support the given @a wire_method.
1061 : *
1062 : * @param master_pub master public key to match exchange by
1063 : * @param wire_method NULL for any
1064 : * @return JSON array with information about all matching accounts
1065 : */
1066 : json_t *
1067 0 : TMH_exchange_accounts_by_method (
1068 : const struct TALER_MasterPublicKeyP *master_pub,
1069 : const char *wire_method)
1070 : {
1071 0 : struct ExchangeMatchContext emc = {
1072 : .wire_method = wire_method,
1073 0 : .accounts = json_array ()
1074 : };
1075 : enum GNUNET_DB_QueryStatus qs;
1076 :
1077 0 : GNUNET_assert (NULL != emc.accounts);
1078 0 : qs = TMH_db->select_accounts_by_exchange (TMH_db->cls,
1079 : master_pub,
1080 : &add_matching_account,
1081 : &emc);
1082 0 : if (qs < 0)
1083 : {
1084 0 : json_decref (emc.accounts);
1085 0 : return NULL;
1086 : }
1087 0 : return emc.accounts;
1088 : }
1089 :
1090 :
1091 : char *
1092 77 : TMH_make_order_status_url (struct MHD_Connection *con,
1093 : const char *order_id,
1094 : const char *session_id,
1095 : const char *instance_id,
1096 : struct TALER_ClaimTokenP *claim_token,
1097 : struct TALER_PrivateContractHashP *h_contract)
1098 : {
1099 : struct GNUNET_Buffer buf;
1100 : /* Number of query parameters written so far */
1101 77 : unsigned int num_qp = 0;
1102 :
1103 77 : GNUNET_assert (NULL != instance_id);
1104 77 : GNUNET_assert (NULL != order_id);
1105 77 : if (GNUNET_OK !=
1106 77 : TMH_base_url_by_connection (con,
1107 : instance_id,
1108 : &buf))
1109 : {
1110 0 : GNUNET_break (0);
1111 0 : return NULL;
1112 : }
1113 77 : GNUNET_buffer_write_path (&buf,
1114 : "/orders");
1115 77 : GNUNET_buffer_write_path (&buf,
1116 : order_id);
1117 77 : if ( (NULL != claim_token) &&
1118 77 : (! GNUNET_is_zero (claim_token)) )
1119 : {
1120 : /* 'token=' for human readability */
1121 63 : GNUNET_buffer_write_str (&buf,
1122 : "?token=");
1123 63 : GNUNET_buffer_write_data_encoded (&buf,
1124 : (char *) claim_token,
1125 : sizeof (*claim_token));
1126 63 : num_qp++;
1127 : }
1128 :
1129 77 : if (NULL != session_id)
1130 : {
1131 16 : if (num_qp > 0)
1132 10 : GNUNET_buffer_write_str (&buf,
1133 : "&session_id=");
1134 : else
1135 6 : GNUNET_buffer_write_str (&buf,
1136 : "?session_id=");
1137 16 : GNUNET_buffer_write_str (&buf,
1138 : session_id);
1139 16 : num_qp++;
1140 : }
1141 :
1142 77 : if (NULL != h_contract)
1143 : {
1144 2 : if (num_qp > 0)
1145 2 : GNUNET_buffer_write_str (&buf,
1146 : "&h_contract=");
1147 : else
1148 0 : GNUNET_buffer_write_str (&buf,
1149 : "?h_contract=");
1150 2 : GNUNET_buffer_write_data_encoded (&buf,
1151 : (char *) h_contract,
1152 : sizeof (*h_contract));
1153 : }
1154 :
1155 77 : return GNUNET_buffer_reap_str (&buf);
1156 : }
1157 :
1158 :
1159 : char *
1160 29 : TMH_make_taler_pay_uri (struct MHD_Connection *con,
1161 : const char *order_id,
1162 : const char *session_id,
1163 : const char *instance_id,
1164 : struct TALER_ClaimTokenP *claim_token)
1165 : {
1166 : struct GNUNET_Buffer buf;
1167 :
1168 29 : GNUNET_assert (NULL != instance_id);
1169 29 : GNUNET_assert (NULL != order_id);
1170 29 : if (GNUNET_OK !=
1171 29 : TMH_taler_uri_by_connection (con,
1172 : "pay",
1173 : instance_id,
1174 : &buf))
1175 : {
1176 0 : GNUNET_break (0);
1177 0 : return NULL;
1178 : }
1179 29 : GNUNET_buffer_write_path (&buf,
1180 : order_id);
1181 29 : GNUNET_buffer_write_path (&buf,
1182 : (NULL == session_id)
1183 : ? ""
1184 : : session_id);
1185 29 : if ( (NULL != claim_token) &&
1186 29 : (! GNUNET_is_zero (claim_token)))
1187 : {
1188 : /* Just 'c=' because this goes into QR
1189 : codes, so this is more compact. */
1190 17 : GNUNET_buffer_write_str (&buf,
1191 : "?c=");
1192 17 : GNUNET_buffer_write_data_encoded (&buf,
1193 : (char *) claim_token,
1194 : sizeof (struct TALER_ClaimTokenP));
1195 : }
1196 :
1197 29 : return GNUNET_buffer_reap_str (&buf);
1198 : }
|