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 98 : TMH_location_object_valid (const json_t *location)
205 : {
206 98 : const char *country = NULL;
207 98 : const char *subdivision = NULL;
208 98 : const char *district = NULL;
209 98 : const char *town = NULL;
210 98 : const char *town_loc = NULL;
211 98 : const char *postcode = NULL;
212 98 : const char *street = NULL;
213 98 : const char *building = NULL;
214 98 : const char *building_no = NULL;
215 98 : const json_t *lines = NULL;
216 : struct GNUNET_JSON_Specification spec[] = {
217 98 : GNUNET_JSON_spec_mark_optional (
218 : GNUNET_JSON_spec_string ("country",
219 : &country),
220 : NULL),
221 98 : GNUNET_JSON_spec_mark_optional (
222 : GNUNET_JSON_spec_string ("country_subdivision",
223 : &subdivision),
224 : NULL),
225 98 : GNUNET_JSON_spec_mark_optional (
226 : GNUNET_JSON_spec_string ("district",
227 : &district),
228 : NULL),
229 98 : GNUNET_JSON_spec_mark_optional (
230 : GNUNET_JSON_spec_string ("town",
231 : &town),
232 : NULL),
233 98 : GNUNET_JSON_spec_mark_optional (
234 : GNUNET_JSON_spec_string ("town_location",
235 : &town_loc),
236 : NULL),
237 98 : GNUNET_JSON_spec_mark_optional (
238 : GNUNET_JSON_spec_string ("post_code",
239 : &postcode),
240 : NULL),
241 98 : GNUNET_JSON_spec_mark_optional (
242 : GNUNET_JSON_spec_string ("street",
243 : &street),
244 : NULL),
245 98 : GNUNET_JSON_spec_mark_optional (
246 : GNUNET_JSON_spec_string ("building_name",
247 : &building),
248 : NULL),
249 98 : GNUNET_JSON_spec_mark_optional (
250 : GNUNET_JSON_spec_string ("building_number",
251 : &building_no),
252 : NULL),
253 98 : GNUNET_JSON_spec_mark_optional (
254 : GNUNET_JSON_spec_array_const ("address_lines",
255 : &lines),
256 : NULL),
257 98 : GNUNET_JSON_spec_end ()
258 : };
259 : const char *ename;
260 : unsigned int eline;
261 :
262 98 : if (GNUNET_OK !=
263 98 : 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 98 : 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 98 : return true;
290 : }
291 :
292 :
293 : bool
294 74 : TMH_products_array_valid (const json_t *products)
295 : {
296 : const json_t *product;
297 : size_t idx;
298 74 : bool valid = true;
299 :
300 74 : if (! json_is_array (products))
301 : {
302 0 : GNUNET_break_op (0);
303 0 : return false;
304 : }
305 97 : 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 74 : 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 21 : 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 21 : 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 21 : wm = GNUNET_new (struct TMH_WireMethod);
511 21 : if (NULL != credit_facade_url)
512 : wm->credit_facade_url
513 2 : = GNUNET_strdup (credit_facade_url);
514 21 : if (NULL != credit_facade_credentials)
515 : wm->credit_facade_credentials
516 2 : = json_incref ((json_t*) credit_facade_credentials);
517 21 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
518 21 : &wm->wire_salt,
519 : sizeof (wm->wire_salt));
520 : wm->payto_uri.full_payto
521 21 : = GNUNET_strdup (payto_uri.full_payto);
522 21 : TALER_merchant_wire_signature_hash (payto_uri,
523 21 : &wm->wire_salt,
524 : &wm->h_wire);
525 : wm->wire_method
526 21 : = TALER_payto_get_method (payto_uri.full_payto);
527 21 : wm->active = true;
528 21 : return wm;
529 : }
530 :
531 :
532 : enum TALER_ErrorCode
533 49 : 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 49 : if (NULL == token)
543 : {
544 6 : *as = TMH_AS_NONE;
545 6 : return TALER_EC_NONE;
546 : }
547 43 : 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 43 : token += strlen (RFC_8959_PREFIX);
555 43 : if (GNUNET_OK !=
556 43 : 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 43 : qs = TMH_db->select_login_token (TMH_db->cls,
568 : instance_id,
569 : &btoken,
570 : &expiration,
571 : (uint32_t*) &scope);
572 43 : if (qs < 0)
573 : {
574 0 : GNUNET_break (0);
575 0 : return TALER_EC_GENERIC_DB_FETCH_FAILED;
576 : }
577 43 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
578 : {
579 9 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
580 : "Authorization token `%s' unknown\n",
581 : token);
582 9 : return TALER_EC_GENERIC_TOKEN_UNKNOWN;
583 : }
584 34 : 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 34 : *as = scope;
592 34 : return TALER_EC_NONE;
593 : }
594 :
595 :
596 : enum GNUNET_GenericReturnValue
597 42 : TMH_check_auth_config (struct MHD_Connection *connection,
598 : const json_t *jauth,
599 : const char **auth_password)
600 : {
601 42 : bool auth_wellformed = false;
602 42 : const char *auth_method = json_string_value (json_object_get (jauth,
603 : "method"));
604 :
605 42 : *auth_password = NULL;
606 42 : if (NULL == auth_method)
607 : {
608 0 : GNUNET_break_op (0);
609 : }
610 42 : else if ((GNUNET_YES != TMH_strict_v19) &&
611 42 : (0 == strcmp (auth_method,
612 : "external")))
613 : {
614 29 : auth_wellformed = true;
615 : }
616 13 : else if (GNUNET_YES == TMH_auth_disabled)
617 : {
618 0 : auth_wellformed = true;
619 : }
620 13 : else if (0 == strcmp (auth_method,
621 : "token"))
622 : {
623 : json_t *pw_value;
624 :
625 13 : pw_value = json_object_get (jauth,
626 : "password");
627 13 : if (NULL == pw_value)
628 : {
629 0 : pw_value = json_object_get (jauth,
630 : "token");
631 : }
632 13 : if (NULL == pw_value)
633 : {
634 0 : auth_wellformed = false;
635 0 : GNUNET_break_op (0);
636 : }
637 : else
638 : {
639 13 : *auth_password = json_string_value (pw_value);
640 13 : if (NULL != *auth_password)
641 : {
642 13 : if (0 == strncasecmp (RFC_8959_PREFIX,
643 : *auth_password,
644 : strlen (RFC_8959_PREFIX)))
645 : {
646 4 : *auth_password = *auth_password + strlen (RFC_8959_PREFIX);
647 : }
648 13 : auth_wellformed = true;
649 : }
650 : }
651 : }
652 :
653 42 : if (! auth_wellformed)
654 : {
655 0 : GNUNET_break_op (0);
656 : return (MHD_YES ==
657 0 : TALER_MHD_reply_with_error (connection,
658 : MHD_HTTP_BAD_REQUEST,
659 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
660 : "bad authentication config"))
661 : ? GNUNET_NO
662 0 : : GNUNET_SYSERR;
663 : }
664 42 : return GNUNET_OK;
665 : }
666 :
667 :
668 : void
669 13 : TMH_uuid_from_string (const char *uuids,
670 : struct GNUNET_Uuid *uuid)
671 : {
672 : struct GNUNET_HashCode hc;
673 :
674 13 : GNUNET_CRYPTO_hash (uuids,
675 : strlen (uuids),
676 : &hc);
677 : GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
678 13 : GNUNET_memcpy (uuid,
679 : &hc,
680 : sizeof (*uuid));
681 13 : }
682 :
683 :
684 : /**
685 : * Closure for #trigger_webhook_cb.
686 : *
687 : * @param instance which is the instance we work with
688 : * @param root JSON data to fill into the template
689 : * @param rv, query of the TALER_TEMPLATEING_fill
690 : */
691 : struct Trigger
692 : {
693 : const char *instance;
694 :
695 : const json_t *root;
696 :
697 : enum GNUNET_DB_QueryStatus rv;
698 :
699 : };
700 :
701 : /**
702 : * Typically called by `TMH_trigger_webhook`.
703 : *
704 : * @param[in,out] cls a `struct Trigger` with information about the webhook
705 : * @param webhook_serial reference to the configured webhook template.
706 : * @param event_type is the event/action of the webhook
707 : * @param url to make request to
708 : * @param http_method use for the webhook
709 : * @param header_template of the webhook
710 : * @param body_template of the webhook
711 : */
712 : static void
713 2 : trigger_webhook_cb (void *cls,
714 : uint64_t webhook_serial,
715 : const char *event_type,
716 : const char *url,
717 : const char *http_method,
718 : const char *header_template,
719 : const char *body_template)
720 : {
721 2 : struct Trigger *t = cls;
722 2 : void *header = NULL;
723 2 : void *body = NULL;
724 : size_t header_size;
725 : size_t body_size;
726 :
727 2 : if (NULL != header_template)
728 : {
729 : int ret;
730 :
731 2 : ret = TALER_TEMPLATING_fill (header_template,
732 : t->root,
733 : &header,
734 : &header_size);
735 2 : if (0 != ret)
736 : {
737 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
738 : "Failed to expand webhook header template for webhook %llu (%d)\n",
739 : (unsigned long long) webhook_serial,
740 : ret);
741 0 : t->rv = GNUNET_DB_STATUS_HARD_ERROR;
742 0 : return;
743 : }
744 : /* Note: header is actually header_size+1 bytes long here, see mustach.c::memfile_close() */
745 2 : GNUNET_assert ('\0' == ((const char *) header)[header_size]);
746 : }
747 2 : if (NULL != body_template)
748 : {
749 : int ret;
750 2 : ret = TALER_TEMPLATING_fill (body_template,
751 : t->root,
752 : &body,
753 : &body_size);
754 2 : if (0 != ret)
755 : {
756 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
757 : "Failed to expand webhook body template for webhook %llu (%d)\n",
758 : (unsigned long long) webhook_serial,
759 : ret);
760 0 : t->rv = GNUNET_DB_STATUS_HARD_ERROR;
761 0 : return;
762 : }
763 : /* Note: body is actually body_size+1 bytes long here, see mustach.c::memfile_close() */
764 2 : GNUNET_assert ('\0' == ((const char *) body)[body_size]);
765 : }
766 2 : t->rv = TMH_db->insert_pending_webhook (TMH_db->cls,
767 : t->instance,
768 : webhook_serial,
769 : url,
770 : http_method,
771 : header,
772 : body);
773 2 : if (t->rv > 0)
774 : {
775 2 : struct GNUNET_DB_EventHeaderP es = {
776 2 : .size = htons (sizeof(es)),
777 2 : .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
778 : };
779 2 : const void *extra = NULL;
780 2 : size_t extra_size = 0;
781 2 : TMH_db->event_notify (TMH_db->cls,
782 : &es,
783 : &extra,
784 : extra_size);
785 : }
786 2 : free (header);
787 2 : free (body);
788 : }
789 :
790 :
791 : /**
792 : * TMH_trigger_webhook is a function that need to be use when someone
793 : * pay. Merchant need to have a notification.
794 : *
795 : * @param instance that we need to send the webhook as a notification
796 : * @param event of the webhook
797 : * @param args argument of the function
798 : */
799 : enum GNUNET_DB_QueryStatus
800 95 : TMH_trigger_webhook (const char *instance,
801 : const char *event,
802 : const json_t *args)
803 : {
804 95 : struct Trigger t = {
805 : .instance = instance,
806 : .root = args
807 : };
808 : enum GNUNET_DB_QueryStatus qs;
809 :
810 95 : qs = TMH_db->lookup_webhook_by_event (TMH_db->cls,
811 : instance,
812 : event,
813 : &trigger_webhook_cb,
814 : &t);
815 95 : if (qs < 0)
816 0 : return qs;
817 95 : return t.rv;
818 : }
819 :
820 :
821 : enum GNUNET_GenericReturnValue
822 146 : TMH_base_url_by_connection (struct MHD_Connection *connection,
823 : const char *instance,
824 : struct GNUNET_Buffer *buf)
825 : {
826 : const char *host;
827 : const char *forwarded_host;
828 : const char *forwarded_port;
829 : const char *uri_path;
830 :
831 146 : memset (buf,
832 : 0,
833 : sizeof (*buf));
834 146 : if (NULL != TMH_base_url)
835 : {
836 0 : GNUNET_buffer_write_str (buf,
837 : TMH_base_url);
838 : }
839 : else
840 : {
841 146 : if (GNUNET_YES ==
842 146 : TALER_mhd_is_https (connection))
843 0 : GNUNET_buffer_write_str (buf,
844 : "https://");
845 : else
846 146 : GNUNET_buffer_write_str (buf,
847 : "http://");
848 146 : host = MHD_lookup_connection_value (connection,
849 : MHD_HEADER_KIND,
850 : MHD_HTTP_HEADER_HOST);
851 146 : forwarded_host = MHD_lookup_connection_value (connection,
852 : MHD_HEADER_KIND,
853 : "X-Forwarded-Host");
854 146 : if (NULL != forwarded_host)
855 : {
856 0 : GNUNET_buffer_write_str (buf,
857 : forwarded_host);
858 : }
859 : else
860 : {
861 146 : if (NULL == host)
862 : {
863 0 : GNUNET_buffer_clear (buf);
864 0 : GNUNET_break (0);
865 0 : return GNUNET_SYSERR;
866 : }
867 146 : GNUNET_buffer_write_str (buf,
868 : host);
869 : }
870 146 : forwarded_port = MHD_lookup_connection_value (connection,
871 : MHD_HEADER_KIND,
872 : "X-Forwarded-Port");
873 146 : if (NULL != forwarded_port)
874 : {
875 0 : GNUNET_buffer_write_str (buf,
876 : ":");
877 0 : GNUNET_buffer_write_str (buf,
878 : forwarded_port);
879 : }
880 146 : uri_path = MHD_lookup_connection_value (connection,
881 : MHD_HEADER_KIND,
882 : "X-Forwarded-Prefix");
883 146 : if (NULL != uri_path)
884 0 : GNUNET_buffer_write_path (buf,
885 : uri_path);
886 : }
887 146 : if (0 != strcmp (instance,
888 : "admin"))
889 : {
890 13 : GNUNET_buffer_write_path (buf,
891 : "/instances/");
892 13 : GNUNET_buffer_write_str (buf,
893 : instance);
894 : }
895 146 : return GNUNET_OK;
896 : }
897 :
898 :
899 : enum GNUNET_GenericReturnValue
900 35 : TMH_taler_uri_by_connection (struct MHD_Connection *connection,
901 : const char *method,
902 : const char *instance,
903 : struct GNUNET_Buffer *buf)
904 : {
905 : const char *host;
906 : const char *forwarded_host;
907 : const char *forwarded_port;
908 : const char *uri_path;
909 :
910 35 : memset (buf,
911 : 0,
912 : sizeof (*buf));
913 35 : host = MHD_lookup_connection_value (connection,
914 : MHD_HEADER_KIND,
915 : "Host");
916 35 : forwarded_host = MHD_lookup_connection_value (connection,
917 : MHD_HEADER_KIND,
918 : "X-Forwarded-Host");
919 35 : forwarded_port = MHD_lookup_connection_value (connection,
920 : MHD_HEADER_KIND,
921 : "X-Forwarded-Port");
922 35 : uri_path = MHD_lookup_connection_value (connection,
923 : MHD_HEADER_KIND,
924 : "X-Forwarded-Prefix");
925 35 : if (NULL != forwarded_host)
926 0 : host = forwarded_host;
927 35 : if (NULL == host)
928 : {
929 0 : GNUNET_break (0);
930 0 : return GNUNET_SYSERR;
931 : }
932 35 : GNUNET_buffer_write_str (buf,
933 : "taler");
934 35 : if (GNUNET_NO == TALER_mhd_is_https (connection))
935 35 : GNUNET_buffer_write_str (buf,
936 : "+http");
937 35 : GNUNET_buffer_write_str (buf,
938 : "://");
939 35 : GNUNET_buffer_write_str (buf,
940 : method);
941 35 : GNUNET_buffer_write_str (buf,
942 : "/");
943 35 : GNUNET_buffer_write_str (buf,
944 : host);
945 35 : if (NULL != forwarded_port)
946 : {
947 0 : GNUNET_buffer_write_str (buf,
948 : ":");
949 0 : GNUNET_buffer_write_str (buf,
950 : forwarded_port);
951 : }
952 35 : if (NULL != uri_path)
953 0 : GNUNET_buffer_write_path (buf,
954 : uri_path);
955 35 : if (0 != strcmp ("admin",
956 : instance))
957 : {
958 3 : GNUNET_buffer_write_path (buf,
959 : "instances");
960 3 : GNUNET_buffer_write_path (buf,
961 : instance);
962 : }
963 35 : return GNUNET_OK;
964 : }
965 :
966 :
967 : /**
968 : * Closure for #add_matching_account().
969 : */
970 : struct ExchangeMatchContext
971 : {
972 : /**
973 : * Wire method to match, NULL for all.
974 : */
975 : const char *wire_method;
976 :
977 : /**
978 : * Array of accounts to return.
979 : */
980 : json_t *accounts;
981 : };
982 :
983 :
984 : /**
985 : * If the given account is feasible, add it to the array
986 : * of accounts we return.
987 : *
988 : * @param cls a `struct PostReserveContext`
989 : * @param payto_uri URI of the account
990 : * @param conversion_url URL of a conversion service
991 : * @param debit_restrictions restrictions for debits from account
992 : * @param credit_restrictions restrictions for credits to account
993 : * @param master_sig signature affirming the account
994 : */
995 : static void
996 0 : add_matching_account (
997 : void *cls,
998 : struct TALER_FullPayto payto_uri,
999 : const char *conversion_url,
1000 : const json_t *debit_restrictions,
1001 : const json_t *credit_restrictions,
1002 : const struct TALER_MasterSignatureP *master_sig)
1003 : {
1004 0 : struct ExchangeMatchContext *rc = cls;
1005 : char *method;
1006 :
1007 0 : method = TALER_payto_get_method (payto_uri.full_payto);
1008 0 : if ( (NULL == rc->wire_method) ||
1009 0 : (0 == strcmp (method,
1010 : rc->wire_method)) )
1011 : {
1012 : json_t *acc;
1013 :
1014 0 : acc = GNUNET_JSON_PACK (
1015 : TALER_JSON_pack_full_payto ("payto_uri",
1016 : payto_uri),
1017 : GNUNET_JSON_pack_data_auto ("master_sig",
1018 : master_sig),
1019 : GNUNET_JSON_pack_allow_null (
1020 : GNUNET_JSON_pack_string ("conversion_url",
1021 : conversion_url)),
1022 : GNUNET_JSON_pack_array_incref ("credit_restrictions",
1023 : (json_t *) credit_restrictions),
1024 : GNUNET_JSON_pack_array_incref ("debit_restrictions",
1025 : (json_t *) debit_restrictions)
1026 : );
1027 0 : GNUNET_assert (0 ==
1028 : json_array_append_new (rc->accounts,
1029 : acc));
1030 : }
1031 0 : GNUNET_free (method);
1032 0 : }
1033 :
1034 :
1035 : /**
1036 : * Return JSON array with all of the exchange accounts
1037 : * that support the given @a wire_method.
1038 : *
1039 : * @param master_pub master public key to match exchange by
1040 : * @param wire_method NULL for any
1041 : * @return JSON array with information about all matching accounts
1042 : */
1043 : json_t *
1044 0 : TMH_exchange_accounts_by_method (
1045 : const struct TALER_MasterPublicKeyP *master_pub,
1046 : const char *wire_method)
1047 : {
1048 0 : struct ExchangeMatchContext emc = {
1049 : .wire_method = wire_method,
1050 0 : .accounts = json_array ()
1051 : };
1052 : enum GNUNET_DB_QueryStatus qs;
1053 :
1054 0 : GNUNET_assert (NULL != emc.accounts);
1055 0 : qs = TMH_db->select_accounts_by_exchange (TMH_db->cls,
1056 : master_pub,
1057 : &add_matching_account,
1058 : &emc);
1059 0 : if (qs < 0)
1060 : {
1061 0 : json_decref (emc.accounts);
1062 0 : return NULL;
1063 : }
1064 0 : return emc.accounts;
1065 : }
1066 :
1067 :
1068 : char *
1069 75 : TMH_make_order_status_url (struct MHD_Connection *con,
1070 : const char *order_id,
1071 : const char *session_id,
1072 : const char *instance_id,
1073 : struct TALER_ClaimTokenP *claim_token,
1074 : struct TALER_PrivateContractHashP *h_contract)
1075 : {
1076 : struct GNUNET_Buffer buf;
1077 : /* Number of query parameters written so far */
1078 75 : unsigned int num_qp = 0;
1079 :
1080 75 : GNUNET_assert (NULL != instance_id);
1081 75 : GNUNET_assert (NULL != order_id);
1082 75 : if (GNUNET_OK !=
1083 75 : TMH_base_url_by_connection (con,
1084 : instance_id,
1085 : &buf))
1086 : {
1087 0 : GNUNET_break (0);
1088 0 : return NULL;
1089 : }
1090 75 : GNUNET_buffer_write_path (&buf,
1091 : "/orders");
1092 75 : GNUNET_buffer_write_path (&buf,
1093 : order_id);
1094 75 : if ( (NULL != claim_token) &&
1095 75 : (! GNUNET_is_zero (claim_token)) )
1096 : {
1097 : /* 'token=' for human readability */
1098 61 : GNUNET_buffer_write_str (&buf,
1099 : "?token=");
1100 61 : GNUNET_buffer_write_data_encoded (&buf,
1101 : (char *) claim_token,
1102 : sizeof (*claim_token));
1103 61 : num_qp++;
1104 : }
1105 :
1106 75 : if (NULL != session_id)
1107 : {
1108 16 : if (num_qp > 0)
1109 10 : GNUNET_buffer_write_str (&buf,
1110 : "&session_id=");
1111 : else
1112 6 : GNUNET_buffer_write_str (&buf,
1113 : "?session_id=");
1114 16 : GNUNET_buffer_write_str (&buf,
1115 : session_id);
1116 16 : num_qp++;
1117 : }
1118 :
1119 75 : if (NULL != h_contract)
1120 : {
1121 2 : if (num_qp > 0)
1122 2 : GNUNET_buffer_write_str (&buf,
1123 : "&h_contract=");
1124 : else
1125 0 : GNUNET_buffer_write_str (&buf,
1126 : "?h_contract=");
1127 2 : GNUNET_buffer_write_data_encoded (&buf,
1128 : (char *) h_contract,
1129 : sizeof (*h_contract));
1130 : }
1131 :
1132 75 : return GNUNET_buffer_reap_str (&buf);
1133 : }
1134 :
1135 :
1136 : char *
1137 29 : TMH_make_taler_pay_uri (struct MHD_Connection *con,
1138 : const char *order_id,
1139 : const char *session_id,
1140 : const char *instance_id,
1141 : struct TALER_ClaimTokenP *claim_token)
1142 : {
1143 : struct GNUNET_Buffer buf;
1144 :
1145 29 : GNUNET_assert (NULL != instance_id);
1146 29 : GNUNET_assert (NULL != order_id);
1147 29 : if (GNUNET_OK !=
1148 29 : TMH_taler_uri_by_connection (con,
1149 : "pay",
1150 : instance_id,
1151 : &buf))
1152 : {
1153 0 : GNUNET_break (0);
1154 0 : return NULL;
1155 : }
1156 29 : GNUNET_buffer_write_path (&buf,
1157 : order_id);
1158 29 : GNUNET_buffer_write_path (&buf,
1159 : (NULL == session_id)
1160 : ? ""
1161 : : session_id);
1162 29 : if ( (NULL != claim_token) &&
1163 29 : (! GNUNET_is_zero (claim_token)))
1164 : {
1165 : /* Just 'c=' because this goes into QR
1166 : codes, so this is more compact. */
1167 17 : GNUNET_buffer_write_str (&buf,
1168 : "?c=");
1169 17 : GNUNET_buffer_write_data_encoded (&buf,
1170 : (char *) claim_token,
1171 : sizeof (struct TALER_ClaimTokenP));
1172 : }
1173 :
1174 29 : return GNUNET_buffer_reap_str (&buf);
1175 : }
|