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