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 : enum GNUNET_GenericReturnValue
31 63 : TMH_parse_fractional_string (const char *value,
32 : int64_t *integer_part,
33 : uint32_t *fractional_part)
34 : {
35 : const char *ptr;
36 63 : uint64_t integer = 0;
37 63 : uint32_t frac = 0;
38 63 : unsigned int digits = 0;
39 :
40 63 : GNUNET_assert (NULL != integer_part);
41 63 : GNUNET_assert (NULL != fractional_part);
42 :
43 63 : if (NULL == value)
44 : {
45 0 : GNUNET_break_op (0);
46 0 : return GNUNET_SYSERR;
47 : }
48 63 : ptr = value;
49 63 : if ('\0' == *ptr)
50 : {
51 0 : GNUNET_break_op (0);
52 0 : return GNUNET_SYSERR;
53 : }
54 63 : if ('-' == *ptr)
55 : {
56 0 : GNUNET_break_op (0);
57 0 : return GNUNET_SYSERR;
58 : }
59 63 : if (! isdigit ((unsigned char) *ptr))
60 : {
61 0 : GNUNET_break_op (0);
62 0 : return GNUNET_SYSERR;
63 : }
64 135 : while (isdigit ((unsigned char) *ptr))
65 : {
66 72 : unsigned int digit = (unsigned int) (*ptr - '0');
67 :
68 72 : if (integer > (UINT64_MAX - digit) / 10)
69 : {
70 0 : GNUNET_break_op (0);
71 0 : return GNUNET_SYSERR;
72 : }
73 72 : integer = integer * 10 + digit;
74 72 : ptr++;
75 : }
76 63 : if ('.' == *ptr)
77 : {
78 12 : ptr++;
79 12 : if ('\0' == *ptr)
80 : {
81 0 : GNUNET_break_op (0);
82 0 : return GNUNET_SYSERR;
83 : }
84 28 : while (isdigit ((unsigned char) *ptr))
85 : {
86 16 : unsigned int digit = (unsigned int) (*ptr - '0');
87 :
88 16 : if (digits >= MERCHANT_UNIT_FRAC_MAX_DIGITS)
89 : {
90 0 : GNUNET_break_op (0);
91 0 : return GNUNET_SYSERR;
92 : }
93 16 : frac = (uint32_t) (frac * 10 + digit);
94 16 : digits++;
95 16 : ptr++;
96 : }
97 68 : while (digits < MERCHANT_UNIT_FRAC_MAX_DIGITS)
98 : {
99 56 : frac *= 10;
100 56 : digits++;
101 : }
102 : }
103 63 : if ('\0' != *ptr)
104 : {
105 0 : GNUNET_break_op (0);
106 0 : return GNUNET_SYSERR;
107 : }
108 63 : if (integer > (uint64_t) INT64_MAX)
109 : {
110 0 : GNUNET_break_op (0);
111 0 : return GNUNET_SYSERR;
112 : }
113 63 : *integer_part = integer;
114 63 : *fractional_part = frac;
115 63 : return GNUNET_OK;
116 : }
117 :
118 :
119 : enum GNUNET_GenericReturnValue
120 65 : TMH_process_quantity_inputs (enum TMH_ValueKind kind,
121 : bool allow_fractional,
122 : bool int_missing,
123 : int64_t int_raw,
124 : bool str_missing,
125 : const char *str_raw,
126 : uint64_t *int_out,
127 : uint32_t *frac_out,
128 : const char **error_param)
129 : {
130 : static char errbuf[128];
131 65 : int64_t parsed_int = 0;
132 65 : uint32_t parsed_frac = 0;
133 65 : const char *int_field = (TMH_VK_STOCK == kind)
134 : ? "total_stock"
135 65 : : "quantity";
136 65 : const char *str_field = (TMH_VK_STOCK == kind)
137 : ? "unit_total_stock"
138 65 : : "unit_quantity";
139 :
140 65 : *error_param = NULL;
141 :
142 65 : if (int_missing && str_missing)
143 : {
144 0 : GNUNET_snprintf (errbuf,
145 : sizeof (errbuf),
146 : "missing %s and %s",
147 : int_field,
148 : str_field);
149 0 : *error_param = errbuf;
150 0 : GNUNET_break_op (0);
151 0 : return GNUNET_SYSERR;
152 : }
153 :
154 65 : if (! str_missing)
155 : {
156 65 : if ( (TMH_VK_STOCK == kind) &&
157 36 : (0 == strcmp ("-1",
158 : str_raw)) )
159 : {
160 2 : parsed_int = -1;
161 2 : parsed_frac = 0;
162 : }
163 : else
164 : {
165 63 : if (GNUNET_OK !=
166 63 : TMH_parse_fractional_string (str_raw,
167 : &parsed_int,
168 : &parsed_frac))
169 : {
170 0 : GNUNET_snprintf (errbuf,
171 : sizeof (errbuf),
172 : "malformed %s",
173 : str_field);
174 0 : *error_param = errbuf;
175 0 : GNUNET_break_op (0);
176 0 : return GNUNET_SYSERR;
177 : }
178 : }
179 : }
180 :
181 65 : if ( (! int_missing) && (! str_missing) )
182 : {
183 1 : if ( (parsed_int != int_raw) || (0 != parsed_frac) )
184 : {
185 0 : GNUNET_snprintf (errbuf,
186 : sizeof (errbuf),
187 : "%s/%s mismatch",
188 : int_field,
189 : str_field);
190 0 : *error_param = errbuf;
191 0 : GNUNET_break_op (0);
192 0 : return GNUNET_SYSERR;
193 : }
194 : }
195 64 : else if (int_missing)
196 : {
197 64 : int_raw = parsed_int;
198 : }
199 :
200 65 : if ( (TMH_VK_STOCK == kind) && (-1 == int_raw) )
201 : {
202 2 : if ( (! str_missing) && (0 != parsed_frac) )
203 : {
204 0 : GNUNET_snprintf (errbuf,
205 : sizeof (errbuf),
206 : "fractional part forbidden with %s='-1'",
207 : str_field);
208 0 : *error_param = errbuf;
209 0 : GNUNET_break_op (0);
210 0 : return GNUNET_SYSERR;
211 : }
212 2 : *int_out = INT64_MAX;
213 2 : *frac_out = INT32_MAX;
214 2 : return GNUNET_OK;
215 : }
216 :
217 63 : if (int_raw < 0)
218 : {
219 0 : GNUNET_snprintf (errbuf,
220 : sizeof (errbuf),
221 : "%s must be non-negative",
222 : int_field);
223 0 : *error_param = errbuf;
224 0 : GNUNET_break_op (0);
225 0 : return GNUNET_SYSERR;
226 : }
227 :
228 63 : if (! allow_fractional)
229 : {
230 51 : if ( (! str_missing) && (0 != parsed_frac) )
231 : {
232 4 : GNUNET_snprintf (errbuf,
233 : sizeof (errbuf),
234 : "fractional part not allowed for %s",
235 : str_field);
236 4 : *error_param = errbuf;
237 4 : GNUNET_break_op (0);
238 4 : return GNUNET_SYSERR;
239 : }
240 47 : parsed_frac = 0;
241 : }
242 12 : else if (! str_missing)
243 : {
244 12 : if (parsed_frac >= MERCHANT_UNIT_FRAC_BASE)
245 : {
246 0 : GNUNET_snprintf (errbuf,
247 : sizeof (errbuf),
248 : "%s fractional part exceeds base %u",
249 : str_field,
250 : MERCHANT_UNIT_FRAC_BASE);
251 0 : *error_param = errbuf;
252 0 : GNUNET_break_op (0);
253 0 : return GNUNET_SYSERR;
254 : }
255 : }
256 :
257 59 : *int_out = (uint64_t) int_raw;
258 59 : *frac_out = parsed_frac;
259 59 : return GNUNET_OK;
260 : }
261 :
262 :
263 : void
264 34 : TMH_format_fractional_string (enum TMH_ValueKind kind,
265 : uint64_t integer,
266 : uint32_t fractional,
267 : size_t buffer_length,
268 : char buffer[static buffer_length])
269 34 : {
270 34 : GNUNET_assert (0 < buffer_length);
271 :
272 34 : if ( (TMH_VK_STOCK == kind) &&
273 1 : (INT64_MAX == (int64_t) integer) &&
274 : (INT32_MAX == (int32_t) fractional) )
275 : {
276 1 : GNUNET_snprintf (buffer,
277 : buffer_length,
278 : "-1");
279 1 : return;
280 : }
281 :
282 33 : GNUNET_assert ( (TMH_VK_QUANTITY != kind) ||
283 : ((INT64_MAX != (int64_t) integer) &&
284 : (INT32_MAX != (int32_t) fractional)) );
285 33 : GNUNET_assert (fractional < MERCHANT_UNIT_FRAC_BASE);
286 :
287 33 : if (0 == fractional)
288 : {
289 25 : GNUNET_snprintf (buffer,
290 : buffer_length,
291 : "%lu",
292 : integer);
293 25 : return;
294 : }
295 : {
296 : char frac_buf[MERCHANT_UNIT_FRAC_MAX_DIGITS + 1];
297 : size_t idx;
298 :
299 8 : GNUNET_snprintf (frac_buf,
300 : sizeof (frac_buf),
301 : "%0*u",
302 : MERCHANT_UNIT_FRAC_MAX_DIGITS,
303 : (unsigned int) fractional);
304 44 : for (idx = strlen (frac_buf); idx > 0; idx--)
305 : {
306 44 : if ('0' != frac_buf[idx - 1])
307 8 : break;
308 36 : frac_buf[idx - 1] = '\0';
309 : }
310 8 : GNUNET_snprintf (buffer,
311 : buffer_length,
312 : "%lu.%s",
313 : integer,
314 : frac_buf);
315 : }
316 : }
317 :
318 :
319 : void
320 0 : TMH_quantity_defaults_from_unit (const struct TMH_MerchantInstance *mi,
321 : const char *unit,
322 : bool *allow_fractional,
323 : uint32_t *precision_level)
324 : {
325 0 : GNUNET_assert (NULL != allow_fractional);
326 0 : GNUNET_assert (NULL != precision_level);
327 0 : if (GNUNET_OK !=
328 0 : TMH_unit_defaults_for_instance (mi,
329 : unit,
330 : allow_fractional,
331 : precision_level))
332 : {
333 0 : *allow_fractional = false;
334 0 : *precision_level = 0;
335 : }
336 0 : }
337 :
338 :
339 : enum GNUNET_GenericReturnValue
340 36 : TMH_unit_defaults_for_instance (const struct TMH_MerchantInstance *mi,
341 : const char *unit,
342 : bool *allow_fractional,
343 : uint32_t *precision_level)
344 : {
345 36 : struct TALER_MERCHANTDB_UnitDetails ud = { 0 };
346 : enum GNUNET_DB_QueryStatus qs;
347 36 : bool allow = false;
348 36 : uint32_t precision = 0;
349 :
350 36 : GNUNET_assert (NULL != allow_fractional);
351 36 : GNUNET_assert (NULL != precision_level);
352 :
353 36 : qs = TMH_db->select_unit (TMH_db->cls,
354 36 : mi->settings.id,
355 : unit,
356 : &ud);
357 36 : switch (qs)
358 : {
359 0 : case GNUNET_DB_STATUS_HARD_ERROR:
360 : case GNUNET_DB_STATUS_SOFT_ERROR:
361 0 : TALER_MERCHANTDB_unit_details_free (&ud);
362 0 : return GNUNET_SYSERR;
363 2 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
364 2 : allow = ud.unit_allow_fraction;
365 2 : precision = ud.unit_precision_level;
366 2 : break;
367 34 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
368 34 : break;
369 0 : default:
370 0 : GNUNET_break (0);
371 0 : TALER_MERCHANTDB_unit_details_free (&ud);
372 0 : return GNUNET_SYSERR;
373 : }
374 36 : TALER_MERCHANTDB_unit_details_free (&ud);
375 :
376 : /* This is definitely not supposed to happen
377 : combination of allow -> false, and precision > 0
378 : yet let's fix it */
379 36 : if (! allow)
380 : {
381 34 : GNUNET_break (0);
382 34 : precision = 0;
383 : }
384 36 : *allow_fractional = allow;
385 36 : *precision_level = precision;
386 36 : return GNUNET_OK;
387 : }
388 :
389 :
390 : enum GNUNET_GenericReturnValue
391 0 : TMH_cmp_wire_account (
392 : const json_t *account,
393 : const struct TMH_WireMethod *wm)
394 : {
395 0 : const char *credit_facade_url = NULL;
396 0 : const json_t *credit_facade_credentials = NULL;
397 : struct TALER_FullPayto uri;
398 : struct GNUNET_JSON_Specification ispec[] = {
399 0 : TALER_JSON_spec_full_payto_uri ("payto_uri",
400 : &uri),
401 0 : GNUNET_JSON_spec_mark_optional (
402 : TALER_JSON_spec_web_url ("credit_facade_url",
403 : &credit_facade_url),
404 : NULL),
405 0 : GNUNET_JSON_spec_mark_optional (
406 : GNUNET_JSON_spec_object_const ("credit_facade_credentials",
407 : &credit_facade_credentials),
408 : NULL),
409 0 : GNUNET_JSON_spec_end ()
410 : };
411 : enum GNUNET_GenericReturnValue res;
412 : const char *ename;
413 : unsigned int eline;
414 :
415 0 : res = GNUNET_JSON_parse (account,
416 : ispec,
417 : &ename,
418 : &eline);
419 0 : if (GNUNET_OK != res)
420 : {
421 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
422 : "Failed to parse account spec: %s (%u)\n",
423 : ename,
424 : eline);
425 0 : return GNUNET_SYSERR;
426 : }
427 0 : if (0 !=
428 0 : TALER_full_payto_cmp (wm->payto_uri,
429 : uri))
430 : {
431 0 : return GNUNET_SYSERR;
432 : }
433 0 : if ( (NULL == credit_facade_url) !=
434 0 : (NULL == wm->credit_facade_url) ||
435 0 : (NULL == credit_facade_credentials) !=
436 0 : (NULL == wm->credit_facade_credentials) )
437 : {
438 0 : return GNUNET_NO;
439 : }
440 0 : if ( (NULL != credit_facade_url) &&
441 0 : (0 != strcmp (credit_facade_url,
442 0 : wm->credit_facade_url)) )
443 : {
444 0 : return GNUNET_NO;
445 : }
446 0 : if ( (NULL != credit_facade_credentials) &&
447 0 : (0 != json_equal (credit_facade_credentials,
448 0 : wm->credit_facade_credentials)) )
449 : {
450 0 : return GNUNET_NO;
451 : }
452 0 : return GNUNET_YES;
453 : }
454 :
455 :
456 : bool
457 0 : TMH_accounts_array_valid (const json_t *accounts)
458 : {
459 : size_t len;
460 :
461 0 : if (! json_is_array (accounts))
462 : {
463 0 : GNUNET_break_op (0);
464 0 : return false;
465 : }
466 0 : len = json_array_size (accounts);
467 0 : for (size_t i = 0; i<len; i++)
468 : {
469 0 : json_t *payto_uri = json_array_get (accounts,
470 : i);
471 0 : const char *credit_facade_url = NULL;
472 0 : const json_t *credit_facade_credentials = NULL;
473 : struct TALER_FullPayto uri;
474 : struct GNUNET_JSON_Specification ispec[] = {
475 0 : TALER_JSON_spec_full_payto_uri ("payto_uri",
476 : &uri),
477 0 : GNUNET_JSON_spec_mark_optional (
478 : TALER_JSON_spec_web_url ("credit_facade_url",
479 : &credit_facade_url),
480 : NULL),
481 0 : GNUNET_JSON_spec_mark_optional (
482 : GNUNET_JSON_spec_object_const ("credit_facade_credentials",
483 : &credit_facade_credentials),
484 : NULL),
485 0 : GNUNET_JSON_spec_end ()
486 : };
487 : enum GNUNET_GenericReturnValue res;
488 : const char *ename;
489 : unsigned int eline;
490 :
491 0 : res = GNUNET_JSON_parse (payto_uri,
492 : ispec,
493 : &ename,
494 : &eline);
495 0 : if (GNUNET_OK != res)
496 : {
497 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
498 : "Failed to parse account spec: %s (%u)\n",
499 : ename,
500 : eline);
501 0 : return false;
502 : }
503 :
504 : /* Test for the same payto:// URI being given twice */
505 0 : for (size_t j = 0; j<i; j++)
506 : {
507 0 : json_t *old_uri = json_array_get (accounts,
508 : j);
509 0 : if (0 == strcmp (uri.full_payto,
510 : json_string_value (
511 0 : json_object_get (old_uri,
512 : "payto_uri"))))
513 : {
514 0 : GNUNET_break_op (0);
515 0 : return false;
516 : }
517 : }
518 : {
519 : char *err;
520 :
521 0 : if (NULL !=
522 0 : (err = TALER_payto_validate (uri)))
523 : {
524 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
525 : "Encountered invalid payto://-URI `%s': %s\n",
526 : uri.full_payto,
527 : err);
528 0 : GNUNET_free (err);
529 0 : return false;
530 : }
531 : }
532 0 : if ( (NULL == credit_facade_url) !=
533 : (NULL == credit_facade_credentials) )
534 : {
535 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
536 : "If credit_facade_url is given, credit_facade_credentials must also be specified (violated for %s)\n",
537 : uri.full_payto);
538 0 : return false;
539 : }
540 0 : if ( (NULL != credit_facade_url) ||
541 0 : (NULL != credit_facade_credentials) )
542 : {
543 : struct TALER_MERCHANT_BANK_AuthenticationData auth;
544 :
545 0 : if (GNUNET_OK !=
546 0 : TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
547 : credit_facade_url,
548 : &auth))
549 : {
550 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
551 : "Invalid credit facade URL or credentials `%s'\n",
552 : credit_facade_url);
553 0 : return false;
554 : }
555 0 : TALER_MERCHANT_BANK_auth_free (&auth);
556 : }
557 : } /* end for all accounts */
558 0 : return true;
559 : }
560 :
561 :
562 : bool
563 110 : TMH_location_object_valid (const json_t *location)
564 : {
565 110 : const char *country = NULL;
566 110 : const char *subdivision = NULL;
567 110 : const char *district = NULL;
568 110 : const char *town = NULL;
569 110 : const char *town_loc = NULL;
570 110 : const char *postcode = NULL;
571 110 : const char *street = NULL;
572 110 : const char *building = NULL;
573 110 : const char *building_no = NULL;
574 110 : const json_t *lines = NULL;
575 : struct GNUNET_JSON_Specification spec[] = {
576 110 : GNUNET_JSON_spec_mark_optional (
577 : GNUNET_JSON_spec_string ("country",
578 : &country),
579 : NULL),
580 110 : GNUNET_JSON_spec_mark_optional (
581 : GNUNET_JSON_spec_string ("country_subdivision",
582 : &subdivision),
583 : NULL),
584 110 : GNUNET_JSON_spec_mark_optional (
585 : GNUNET_JSON_spec_string ("district",
586 : &district),
587 : NULL),
588 110 : GNUNET_JSON_spec_mark_optional (
589 : GNUNET_JSON_spec_string ("town",
590 : &town),
591 : NULL),
592 110 : GNUNET_JSON_spec_mark_optional (
593 : GNUNET_JSON_spec_string ("town_location",
594 : &town_loc),
595 : NULL),
596 110 : GNUNET_JSON_spec_mark_optional (
597 : GNUNET_JSON_spec_string ("post_code",
598 : &postcode),
599 : NULL),
600 110 : GNUNET_JSON_spec_mark_optional (
601 : GNUNET_JSON_spec_string ("street",
602 : &street),
603 : NULL),
604 110 : GNUNET_JSON_spec_mark_optional (
605 : GNUNET_JSON_spec_string ("building_name",
606 : &building),
607 : NULL),
608 110 : GNUNET_JSON_spec_mark_optional (
609 : GNUNET_JSON_spec_string ("building_number",
610 : &building_no),
611 : NULL),
612 110 : GNUNET_JSON_spec_mark_optional (
613 : GNUNET_JSON_spec_array_const ("address_lines",
614 : &lines),
615 : NULL),
616 110 : GNUNET_JSON_spec_end ()
617 : };
618 : const char *ename;
619 : unsigned int eline;
620 :
621 110 : if (GNUNET_OK !=
622 110 : GNUNET_JSON_parse (location,
623 : spec,
624 : &ename,
625 : &eline))
626 : {
627 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
628 : "Invalid location for field %s\n",
629 : ename);
630 0 : return false;
631 : }
632 110 : if (NULL != lines)
633 : {
634 : size_t idx;
635 : json_t *line;
636 :
637 0 : json_array_foreach (lines, idx, line)
638 : {
639 0 : if (! json_is_string (line))
640 : {
641 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
642 : "Invalid address line #%u in location\n",
643 : (unsigned int) idx);
644 0 : return false;
645 : }
646 : }
647 : }
648 110 : return true;
649 : }
650 :
651 :
652 : bool
653 76 : TMH_products_array_valid (const json_t *products)
654 : {
655 : const json_t *product;
656 : size_t idx;
657 76 : bool valid = true;
658 :
659 76 : if (! json_is_array (products))
660 : {
661 0 : GNUNET_break_op (0);
662 0 : return false;
663 : }
664 101 : json_array_foreach ((json_t *) products, idx, product)
665 : {
666 25 : const char *product_id = NULL;
667 : const char *description;
668 25 : uint64_t quantity = 0;
669 25 : bool quantity_missing = true;
670 25 : const char *unit_quantity = NULL;
671 25 : bool unit_quantity_missing = true;
672 25 : const char *unit = NULL;
673 25 : struct TALER_Amount price = { .value = 0 };
674 25 : const char *image_data_url = NULL;
675 25 : const json_t *taxes = NULL;
676 25 : struct GNUNET_TIME_Timestamp delivery_date = { 0 };
677 : struct GNUNET_JSON_Specification spec[] = {
678 25 : GNUNET_JSON_spec_mark_optional (
679 : GNUNET_JSON_spec_string ("product_id",
680 : &product_id),
681 : NULL),
682 25 : TALER_JSON_spec_i18n_str ("description",
683 : &description),
684 25 : GNUNET_JSON_spec_mark_optional (
685 : GNUNET_JSON_spec_uint64 ("quantity",
686 : &quantity),
687 : &quantity_missing),
688 25 : GNUNET_JSON_spec_mark_optional (
689 : GNUNET_JSON_spec_string ("unit_quantity",
690 : &unit_quantity),
691 : &unit_quantity_missing),
692 25 : GNUNET_JSON_spec_mark_optional (
693 : GNUNET_JSON_spec_string ("unit",
694 : &unit),
695 : NULL),
696 25 : GNUNET_JSON_spec_mark_optional (
697 : TALER_JSON_spec_amount_any ("price",
698 : &price),
699 : NULL),
700 25 : GNUNET_JSON_spec_mark_optional (
701 : GNUNET_JSON_spec_string ("image",
702 : &image_data_url),
703 : NULL),
704 25 : GNUNET_JSON_spec_mark_optional (
705 : GNUNET_JSON_spec_array_const ("taxes",
706 : &taxes),
707 : NULL),
708 25 : GNUNET_JSON_spec_mark_optional (
709 : GNUNET_JSON_spec_timestamp ("delivery_date",
710 : &delivery_date),
711 : NULL),
712 25 : GNUNET_JSON_spec_end ()
713 : };
714 : const char *ename;
715 : unsigned int eline;
716 :
717 25 : if (GNUNET_OK !=
718 25 : GNUNET_JSON_parse (product,
719 : spec,
720 : &ename,
721 : &eline))
722 : {
723 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
724 : "Invalid product #%u for field %s\n",
725 : (unsigned int) idx,
726 : ename);
727 0 : return false;
728 : }
729 25 : if (quantity_missing)
730 : {
731 0 : if (unit_quantity_missing)
732 : {
733 0 : GNUNET_break_op (0);
734 0 : valid = false;
735 : }
736 : }
737 25 : if ( (NULL != image_data_url) &&
738 23 : (! TMH_image_data_url_valid (image_data_url)) )
739 : {
740 0 : GNUNET_break_op (0);
741 0 : valid = false;
742 : }
743 25 : if ( (NULL != taxes) &&
744 23 : (! TMH_taxes_array_valid (taxes)) )
745 : {
746 0 : GNUNET_break_op (0);
747 0 : valid = false;
748 : }
749 25 : GNUNET_JSON_parse_free (spec);
750 25 : if (! valid)
751 0 : break;
752 : }
753 :
754 76 : return valid;
755 : }
756 :
757 :
758 : bool
759 59 : TMH_image_data_url_valid (const char *image_data_url)
760 : {
761 59 : if (0 == strcmp (image_data_url,
762 : ""))
763 21 : return true;
764 38 : if (0 != strncasecmp ("data:image/",
765 : image_data_url,
766 : strlen ("data:image/")))
767 : {
768 0 : GNUNET_break_op (0);
769 0 : return false;
770 : }
771 38 : if (NULL == strstr (image_data_url,
772 : ";base64,"))
773 : {
774 0 : GNUNET_break_op (0);
775 0 : return false;
776 : }
777 38 : if (! TALER_url_valid_charset (image_data_url))
778 : {
779 0 : GNUNET_break_op (0);
780 0 : return false;
781 : }
782 38 : return true;
783 : }
784 :
785 :
786 : bool
787 16 : TMH_template_contract_valid (const json_t *template_contract)
788 : {
789 : const char *summary;
790 : const char *currency;
791 16 : struct TALER_Amount amount = { .value = 0};
792 16 : uint32_t minimum_age = 0;
793 16 : struct GNUNET_TIME_Relative pay_duration = { 0 };
794 : struct GNUNET_JSON_Specification spec[] = {
795 16 : GNUNET_JSON_spec_mark_optional (
796 : GNUNET_JSON_spec_string ("summary",
797 : &summary),
798 : NULL),
799 16 : GNUNET_JSON_spec_mark_optional (
800 : GNUNET_JSON_spec_string ("currency",
801 : ¤cy),
802 : NULL),
803 16 : GNUNET_JSON_spec_mark_optional (
804 : TALER_JSON_spec_amount_any ("amount",
805 : &amount),
806 : NULL),
807 16 : GNUNET_JSON_spec_uint32 ("minimum_age",
808 : &minimum_age),
809 16 : GNUNET_JSON_spec_relative_time ("pay_duration",
810 : &pay_duration),
811 16 : GNUNET_JSON_spec_end ()
812 : };
813 : const char *ename;
814 : unsigned int eline;
815 :
816 16 : if (GNUNET_OK !=
817 16 : GNUNET_JSON_parse (template_contract,
818 : spec,
819 : &ename,
820 : &eline))
821 : {
822 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
823 : "Invalid template_contract for field %s\n",
824 : ename);
825 0 : return false;
826 : }
827 16 : return true;
828 : }
829 :
830 :
831 : bool
832 59 : TMH_taxes_array_valid (const json_t *taxes)
833 : {
834 : json_t *tax;
835 : size_t idx;
836 :
837 59 : if (! json_is_array (taxes))
838 0 : return false;
839 59 : json_array_foreach (taxes, idx, tax)
840 : {
841 : struct TALER_Amount amount;
842 : const char *name;
843 : struct GNUNET_JSON_Specification spec[] = {
844 0 : GNUNET_JSON_spec_string ("name",
845 : &name),
846 0 : TALER_JSON_spec_amount_any ("tax",
847 : &amount),
848 0 : GNUNET_JSON_spec_end ()
849 : };
850 : enum GNUNET_GenericReturnValue res;
851 :
852 0 : res = TALER_MHD_parse_json_data (NULL,
853 : tax,
854 : spec);
855 0 : if (GNUNET_OK != res)
856 : {
857 0 : GNUNET_break_op (0);
858 0 : return false;
859 : }
860 : }
861 59 : return true;
862 : }
863 :
864 :
865 : struct TMH_WireMethod *
866 21 : TMH_setup_wire_account (
867 : struct TALER_FullPayto payto_uri,
868 : const char *credit_facade_url,
869 : const json_t *credit_facade_credentials)
870 : {
871 : struct TMH_WireMethod *wm;
872 : char *emsg;
873 :
874 21 : if (NULL != (emsg = TALER_payto_validate (payto_uri)))
875 : {
876 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
877 : "Invalid URI `%s': %s\n",
878 : payto_uri.full_payto,
879 : emsg);
880 0 : GNUNET_free (emsg);
881 0 : return NULL;
882 : }
883 :
884 21 : wm = GNUNET_new (struct TMH_WireMethod);
885 21 : if (NULL != credit_facade_url)
886 : wm->credit_facade_url
887 2 : = GNUNET_strdup (credit_facade_url);
888 21 : if (NULL != credit_facade_credentials)
889 : wm->credit_facade_credentials
890 2 : = json_incref ((json_t*) credit_facade_credentials);
891 21 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
892 21 : &wm->wire_salt,
893 : sizeof (wm->wire_salt));
894 : wm->payto_uri.full_payto
895 21 : = GNUNET_strdup (payto_uri.full_payto);
896 21 : TALER_merchant_wire_signature_hash (payto_uri,
897 21 : &wm->wire_salt,
898 : &wm->h_wire);
899 : wm->wire_method
900 21 : = TALER_payto_get_method (payto_uri.full_payto);
901 21 : wm->active = true;
902 21 : return wm;
903 : }
904 :
905 :
906 : enum TALER_ErrorCode
907 49 : TMH_check_token (const char *token,
908 : const char *instance_id,
909 : enum TMH_AuthScope *as)
910 : {
911 : enum TMH_AuthScope scope;
912 : struct GNUNET_TIME_Timestamp expiration;
913 : enum GNUNET_DB_QueryStatus qs;
914 : struct TALER_MERCHANTDB_LoginTokenP btoken;
915 :
916 49 : if (NULL == token)
917 : {
918 6 : *as = TMH_AS_NONE;
919 6 : return TALER_EC_NONE;
920 : }
921 43 : if (0 != strncasecmp (token,
922 : RFC_8959_PREFIX,
923 : strlen (RFC_8959_PREFIX)))
924 : {
925 0 : *as = TMH_AS_NONE;
926 0 : return TALER_EC_NONE;
927 : }
928 43 : token += strlen (RFC_8959_PREFIX);
929 43 : if (GNUNET_OK !=
930 43 : GNUNET_STRINGS_string_to_data (token,
931 : strlen (token),
932 : &btoken,
933 : sizeof (btoken)))
934 : {
935 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
936 : "Given authorization token `%s' is malformed\n",
937 : token);
938 0 : GNUNET_break_op (0);
939 0 : return TALER_EC_GENERIC_TOKEN_MALFORMED;
940 : }
941 43 : qs = TMH_db->select_login_token (TMH_db->cls,
942 : instance_id,
943 : &btoken,
944 : &expiration,
945 : (uint32_t*) &scope);
946 43 : if (qs < 0)
947 : {
948 0 : GNUNET_break (0);
949 0 : return TALER_EC_GENERIC_DB_FETCH_FAILED;
950 : }
951 43 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
952 : {
953 9 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
954 : "Authorization token `%s' unknown\n",
955 : token);
956 9 : return TALER_EC_GENERIC_TOKEN_UNKNOWN;
957 : }
958 34 : if (GNUNET_TIME_absolute_is_past (expiration.abs_time))
959 : {
960 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
961 : "Authorization token `%s' expired\n",
962 : token);
963 0 : return TALER_EC_GENERIC_TOKEN_EXPIRED;
964 : }
965 34 : *as = scope;
966 34 : return TALER_EC_NONE;
967 : }
968 :
969 :
970 : enum GNUNET_GenericReturnValue
971 42 : TMH_check_auth_config (struct MHD_Connection *connection,
972 : const json_t *jauth,
973 : const char **auth_password)
974 : {
975 42 : bool auth_wellformed = false;
976 42 : const char *auth_method = json_string_value (json_object_get (jauth,
977 : "method"));
978 :
979 42 : *auth_password = NULL;
980 42 : if (NULL == auth_method)
981 : {
982 0 : GNUNET_break_op (0);
983 : }
984 42 : else if ((GNUNET_YES != TMH_strict_v19) &&
985 42 : (0 == strcmp (auth_method,
986 : "external")))
987 : {
988 29 : auth_wellformed = true;
989 : }
990 13 : else if (GNUNET_YES == TMH_auth_disabled)
991 : {
992 0 : auth_wellformed = true;
993 : }
994 13 : else if (0 == strcmp (auth_method,
995 : "token"))
996 : {
997 : json_t *pw_value;
998 :
999 13 : pw_value = json_object_get (jauth,
1000 : "password");
1001 13 : if (NULL == pw_value)
1002 : {
1003 0 : pw_value = json_object_get (jauth,
1004 : "token");
1005 : }
1006 13 : if (NULL == pw_value)
1007 : {
1008 0 : auth_wellformed = false;
1009 0 : GNUNET_break_op (0);
1010 : }
1011 : else
1012 : {
1013 13 : *auth_password = json_string_value (pw_value);
1014 13 : if (NULL != *auth_password)
1015 : {
1016 13 : if (0 == strncasecmp (RFC_8959_PREFIX,
1017 : *auth_password,
1018 : strlen (RFC_8959_PREFIX)))
1019 : {
1020 4 : *auth_password = *auth_password + strlen (RFC_8959_PREFIX);
1021 : }
1022 13 : auth_wellformed = true;
1023 : }
1024 : }
1025 : }
1026 :
1027 42 : if (! auth_wellformed)
1028 : {
1029 0 : GNUNET_break_op (0);
1030 : return (MHD_YES ==
1031 0 : TALER_MHD_reply_with_error (connection,
1032 : MHD_HTTP_BAD_REQUEST,
1033 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
1034 : "bad authentication config"))
1035 : ? GNUNET_NO
1036 0 : : GNUNET_SYSERR;
1037 : }
1038 42 : return GNUNET_OK;
1039 : }
1040 :
1041 :
1042 : void
1043 17 : TMH_uuid_from_string (const char *uuids,
1044 : struct GNUNET_Uuid *uuid)
1045 : {
1046 : struct GNUNET_HashCode hc;
1047 :
1048 17 : GNUNET_CRYPTO_hash (uuids,
1049 : strlen (uuids),
1050 : &hc);
1051 : GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
1052 17 : GNUNET_memcpy (uuid,
1053 : &hc,
1054 : sizeof (*uuid));
1055 17 : }
1056 :
1057 :
1058 : /**
1059 : * Closure for #trigger_webhook_cb.
1060 : *
1061 : * @param instance which is the instance we work with
1062 : * @param root JSON data to fill into the template
1063 : * @param rv, query of the TALER_TEMPLATEING_fill
1064 : */
1065 : struct Trigger
1066 : {
1067 : const char *instance;
1068 :
1069 : const json_t *root;
1070 :
1071 : enum GNUNET_DB_QueryStatus rv;
1072 :
1073 : };
1074 :
1075 : /**
1076 : * Typically called by `TMH_trigger_webhook`.
1077 : *
1078 : * @param[in,out] cls a `struct Trigger` with information about the webhook
1079 : * @param webhook_serial reference to the configured webhook template.
1080 : * @param event_type is the event/action of the webhook
1081 : * @param url to make request to
1082 : * @param http_method use for the webhook
1083 : * @param header_template of the webhook
1084 : * @param body_template of the webhook
1085 : */
1086 : static void
1087 4 : trigger_webhook_cb (void *cls,
1088 : uint64_t webhook_serial,
1089 : const char *event_type,
1090 : const char *url,
1091 : const char *http_method,
1092 : const char *header_template,
1093 : const char *body_template)
1094 : {
1095 4 : struct Trigger *t = cls;
1096 4 : void *header = NULL;
1097 4 : void *body = NULL;
1098 : size_t header_size;
1099 : size_t body_size;
1100 :
1101 4 : if (NULL != header_template)
1102 : {
1103 : int ret;
1104 :
1105 4 : ret = TALER_TEMPLATING_fill (header_template,
1106 : t->root,
1107 : &header,
1108 : &header_size);
1109 4 : if (0 != ret)
1110 : {
1111 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1112 : "Failed to expand webhook header template for webhook %llu (%d)\n",
1113 : (unsigned long long) webhook_serial,
1114 : ret);
1115 0 : t->rv = GNUNET_DB_STATUS_HARD_ERROR;
1116 0 : return;
1117 : }
1118 : /* Note: header is actually header_size+1 bytes long here, see mustach.c::memfile_close() */
1119 4 : GNUNET_assert ('\0' == ((const char *) header)[header_size]);
1120 : }
1121 4 : if (NULL != body_template)
1122 : {
1123 : int ret;
1124 4 : ret = TALER_TEMPLATING_fill (body_template,
1125 : t->root,
1126 : &body,
1127 : &body_size);
1128 4 : if (0 != ret)
1129 : {
1130 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1131 : "Failed to expand webhook body template for webhook %llu (%d)\n",
1132 : (unsigned long long) webhook_serial,
1133 : ret);
1134 0 : t->rv = GNUNET_DB_STATUS_HARD_ERROR;
1135 0 : return;
1136 : }
1137 : /* Note: body is actually body_size+1 bytes long here, see mustach.c::memfile_close() */
1138 4 : GNUNET_assert ('\0' == ((const char *) body)[body_size]);
1139 : }
1140 4 : t->rv = TMH_db->insert_pending_webhook (TMH_db->cls,
1141 : t->instance,
1142 : webhook_serial,
1143 : url,
1144 : http_method,
1145 : header,
1146 : body);
1147 4 : if (t->rv > 0)
1148 : {
1149 4 : struct GNUNET_DB_EventHeaderP es = {
1150 4 : .size = htons (sizeof(es)),
1151 4 : .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
1152 : };
1153 4 : const void *extra = NULL;
1154 4 : size_t extra_size = 0;
1155 4 : TMH_db->event_notify (TMH_db->cls,
1156 : &es,
1157 : &extra,
1158 : extra_size);
1159 : }
1160 4 : free (header);
1161 4 : free (body);
1162 : }
1163 :
1164 :
1165 : /**
1166 : * TMH_trigger_webhook is a function that need to be use when someone
1167 : * pay. Merchant need to have a notification.
1168 : *
1169 : * @param instance that we need to send the webhook as a notification
1170 : * @param event of the webhook
1171 : * @param args argument of the function
1172 : */
1173 : enum GNUNET_DB_QueryStatus
1174 99 : TMH_trigger_webhook (const char *instance,
1175 : const char *event,
1176 : const json_t *args)
1177 : {
1178 99 : struct Trigger t = {
1179 : .instance = instance,
1180 : .root = args
1181 : };
1182 : enum GNUNET_DB_QueryStatus qs;
1183 :
1184 99 : qs = TMH_db->lookup_webhook_by_event (TMH_db->cls,
1185 : instance,
1186 : event,
1187 : &trigger_webhook_cb,
1188 : &t);
1189 99 : if (qs < 0)
1190 0 : return qs;
1191 99 : return t.rv;
1192 : }
1193 :
1194 :
1195 : enum GNUNET_GenericReturnValue
1196 152 : TMH_base_url_by_connection (struct MHD_Connection *connection,
1197 : const char *instance,
1198 : struct GNUNET_Buffer *buf)
1199 : {
1200 : const char *host;
1201 : const char *forwarded_host;
1202 : const char *forwarded_port;
1203 : const char *uri_path;
1204 :
1205 152 : memset (buf,
1206 : 0,
1207 : sizeof (*buf));
1208 152 : if (NULL != TMH_base_url)
1209 : {
1210 0 : GNUNET_buffer_write_str (buf,
1211 : TMH_base_url);
1212 : }
1213 : else
1214 : {
1215 152 : if (GNUNET_YES ==
1216 152 : TALER_mhd_is_https (connection))
1217 0 : GNUNET_buffer_write_str (buf,
1218 : "https://");
1219 : else
1220 152 : GNUNET_buffer_write_str (buf,
1221 : "http://");
1222 152 : host = MHD_lookup_connection_value (connection,
1223 : MHD_HEADER_KIND,
1224 : MHD_HTTP_HEADER_HOST);
1225 152 : forwarded_host = MHD_lookup_connection_value (connection,
1226 : MHD_HEADER_KIND,
1227 : "X-Forwarded-Host");
1228 152 : if (NULL != forwarded_host)
1229 : {
1230 0 : GNUNET_buffer_write_str (buf,
1231 : forwarded_host);
1232 : }
1233 : else
1234 : {
1235 152 : if (NULL == host)
1236 : {
1237 0 : GNUNET_buffer_clear (buf);
1238 0 : GNUNET_break (0);
1239 0 : return GNUNET_SYSERR;
1240 : }
1241 152 : GNUNET_buffer_write_str (buf,
1242 : host);
1243 : }
1244 152 : forwarded_port = MHD_lookup_connection_value (connection,
1245 : MHD_HEADER_KIND,
1246 : "X-Forwarded-Port");
1247 152 : if (NULL != forwarded_port)
1248 : {
1249 0 : GNUNET_buffer_write_str (buf,
1250 : ":");
1251 0 : GNUNET_buffer_write_str (buf,
1252 : forwarded_port);
1253 : }
1254 152 : uri_path = MHD_lookup_connection_value (connection,
1255 : MHD_HEADER_KIND,
1256 : "X-Forwarded-Prefix");
1257 152 : if (NULL != uri_path)
1258 0 : GNUNET_buffer_write_path (buf,
1259 : uri_path);
1260 : }
1261 152 : if (0 != strcmp (instance,
1262 : "admin"))
1263 : {
1264 13 : GNUNET_buffer_write_path (buf,
1265 : "/instances/");
1266 13 : GNUNET_buffer_write_str (buf,
1267 : instance);
1268 : }
1269 152 : return GNUNET_OK;
1270 : }
1271 :
1272 :
1273 : enum GNUNET_GenericReturnValue
1274 35 : TMH_taler_uri_by_connection (struct MHD_Connection *connection,
1275 : const char *method,
1276 : const char *instance,
1277 : struct GNUNET_Buffer *buf)
1278 : {
1279 : const char *host;
1280 : const char *forwarded_host;
1281 : const char *forwarded_port;
1282 : const char *uri_path;
1283 :
1284 35 : memset (buf,
1285 : 0,
1286 : sizeof (*buf));
1287 35 : host = MHD_lookup_connection_value (connection,
1288 : MHD_HEADER_KIND,
1289 : "Host");
1290 35 : forwarded_host = MHD_lookup_connection_value (connection,
1291 : MHD_HEADER_KIND,
1292 : "X-Forwarded-Host");
1293 35 : forwarded_port = MHD_lookup_connection_value (connection,
1294 : MHD_HEADER_KIND,
1295 : "X-Forwarded-Port");
1296 35 : uri_path = MHD_lookup_connection_value (connection,
1297 : MHD_HEADER_KIND,
1298 : "X-Forwarded-Prefix");
1299 35 : if (NULL != forwarded_host)
1300 0 : host = forwarded_host;
1301 35 : if (NULL == host)
1302 : {
1303 0 : GNUNET_break (0);
1304 0 : return GNUNET_SYSERR;
1305 : }
1306 35 : GNUNET_buffer_write_str (buf,
1307 : "taler");
1308 35 : if (GNUNET_NO == TALER_mhd_is_https (connection))
1309 35 : GNUNET_buffer_write_str (buf,
1310 : "+http");
1311 35 : GNUNET_buffer_write_str (buf,
1312 : "://");
1313 35 : GNUNET_buffer_write_str (buf,
1314 : method);
1315 35 : GNUNET_buffer_write_str (buf,
1316 : "/");
1317 35 : GNUNET_buffer_write_str (buf,
1318 : host);
1319 35 : if (NULL != forwarded_port)
1320 : {
1321 0 : GNUNET_buffer_write_str (buf,
1322 : ":");
1323 0 : GNUNET_buffer_write_str (buf,
1324 : forwarded_port);
1325 : }
1326 35 : if (NULL != uri_path)
1327 0 : GNUNET_buffer_write_path (buf,
1328 : uri_path);
1329 35 : if (0 != strcmp ("admin",
1330 : instance))
1331 : {
1332 3 : GNUNET_buffer_write_path (buf,
1333 : "instances");
1334 3 : GNUNET_buffer_write_path (buf,
1335 : instance);
1336 : }
1337 35 : return GNUNET_OK;
1338 : }
1339 :
1340 :
1341 : /**
1342 : * Closure for #add_matching_account().
1343 : */
1344 : struct ExchangeMatchContext
1345 : {
1346 : /**
1347 : * Wire method to match, NULL for all.
1348 : */
1349 : const char *wire_method;
1350 :
1351 : /**
1352 : * Array of accounts to return.
1353 : */
1354 : json_t *accounts;
1355 : };
1356 :
1357 :
1358 : /**
1359 : * If the given account is feasible, add it to the array
1360 : * of accounts we return.
1361 : *
1362 : * @param cls a `struct PostReserveContext`
1363 : * @param payto_uri URI of the account
1364 : * @param conversion_url URL of a conversion service
1365 : * @param debit_restrictions restrictions for debits from account
1366 : * @param credit_restrictions restrictions for credits to account
1367 : * @param master_sig signature affirming the account
1368 : */
1369 : static void
1370 0 : add_matching_account (
1371 : void *cls,
1372 : struct TALER_FullPayto payto_uri,
1373 : const char *conversion_url,
1374 : const json_t *debit_restrictions,
1375 : const json_t *credit_restrictions,
1376 : const struct TALER_MasterSignatureP *master_sig)
1377 : {
1378 0 : struct ExchangeMatchContext *rc = cls;
1379 : char *method;
1380 :
1381 0 : method = TALER_payto_get_method (payto_uri.full_payto);
1382 0 : if ( (NULL == rc->wire_method) ||
1383 0 : (0 == strcmp (method,
1384 : rc->wire_method)) )
1385 : {
1386 : json_t *acc;
1387 :
1388 0 : acc = GNUNET_JSON_PACK (
1389 : TALER_JSON_pack_full_payto ("payto_uri",
1390 : payto_uri),
1391 : GNUNET_JSON_pack_data_auto ("master_sig",
1392 : master_sig),
1393 : GNUNET_JSON_pack_allow_null (
1394 : GNUNET_JSON_pack_string ("conversion_url",
1395 : conversion_url)),
1396 : GNUNET_JSON_pack_array_incref ("credit_restrictions",
1397 : (json_t *) credit_restrictions),
1398 : GNUNET_JSON_pack_array_incref ("debit_restrictions",
1399 : (json_t *) debit_restrictions)
1400 : );
1401 0 : GNUNET_assert (0 ==
1402 : json_array_append_new (rc->accounts,
1403 : acc));
1404 : }
1405 0 : GNUNET_free (method);
1406 0 : }
1407 :
1408 :
1409 : /**
1410 : * Return JSON array with all of the exchange accounts
1411 : * that support the given @a wire_method.
1412 : *
1413 : * @param master_pub master public key to match exchange by
1414 : * @param wire_method NULL for any
1415 : * @return JSON array with information about all matching accounts
1416 : */
1417 : json_t *
1418 0 : TMH_exchange_accounts_by_method (
1419 : const struct TALER_MasterPublicKeyP *master_pub,
1420 : const char *wire_method)
1421 : {
1422 0 : struct ExchangeMatchContext emc = {
1423 : .wire_method = wire_method,
1424 0 : .accounts = json_array ()
1425 : };
1426 : enum GNUNET_DB_QueryStatus qs;
1427 :
1428 0 : GNUNET_assert (NULL != emc.accounts);
1429 0 : qs = TMH_db->select_accounts_by_exchange (TMH_db->cls,
1430 : master_pub,
1431 : &add_matching_account,
1432 : &emc);
1433 0 : if (qs < 0)
1434 : {
1435 0 : json_decref (emc.accounts);
1436 0 : return NULL;
1437 : }
1438 0 : return emc.accounts;
1439 : }
1440 :
1441 :
1442 : char *
1443 77 : TMH_make_order_status_url (struct MHD_Connection *con,
1444 : const char *order_id,
1445 : const char *session_id,
1446 : const char *instance_id,
1447 : struct TALER_ClaimTokenP *claim_token,
1448 : struct TALER_PrivateContractHashP *h_contract)
1449 : {
1450 : struct GNUNET_Buffer buf;
1451 : /* Number of query parameters written so far */
1452 77 : unsigned int num_qp = 0;
1453 :
1454 77 : GNUNET_assert (NULL != instance_id);
1455 77 : GNUNET_assert (NULL != order_id);
1456 77 : if (GNUNET_OK !=
1457 77 : TMH_base_url_by_connection (con,
1458 : instance_id,
1459 : &buf))
1460 : {
1461 0 : GNUNET_break (0);
1462 0 : return NULL;
1463 : }
1464 77 : GNUNET_buffer_write_path (&buf,
1465 : "/orders");
1466 77 : GNUNET_buffer_write_path (&buf,
1467 : order_id);
1468 77 : if ( (NULL != claim_token) &&
1469 77 : (! GNUNET_is_zero (claim_token)) )
1470 : {
1471 : /* 'token=' for human readability */
1472 63 : GNUNET_buffer_write_str (&buf,
1473 : "?token=");
1474 63 : GNUNET_buffer_write_data_encoded (&buf,
1475 : (char *) claim_token,
1476 : sizeof (*claim_token));
1477 63 : num_qp++;
1478 : }
1479 :
1480 77 : if (NULL != session_id)
1481 : {
1482 16 : if (num_qp > 0)
1483 10 : GNUNET_buffer_write_str (&buf,
1484 : "&session_id=");
1485 : else
1486 6 : GNUNET_buffer_write_str (&buf,
1487 : "?session_id=");
1488 16 : GNUNET_buffer_write_str (&buf,
1489 : session_id);
1490 16 : num_qp++;
1491 : }
1492 :
1493 77 : if (NULL != h_contract)
1494 : {
1495 2 : if (num_qp > 0)
1496 2 : GNUNET_buffer_write_str (&buf,
1497 : "&h_contract=");
1498 : else
1499 0 : GNUNET_buffer_write_str (&buf,
1500 : "?h_contract=");
1501 2 : GNUNET_buffer_write_data_encoded (&buf,
1502 : (char *) h_contract,
1503 : sizeof (*h_contract));
1504 : }
1505 :
1506 77 : return GNUNET_buffer_reap_str (&buf);
1507 : }
1508 :
1509 :
1510 : char *
1511 29 : TMH_make_taler_pay_uri (struct MHD_Connection *con,
1512 : const char *order_id,
1513 : const char *session_id,
1514 : const char *instance_id,
1515 : struct TALER_ClaimTokenP *claim_token)
1516 : {
1517 : struct GNUNET_Buffer buf;
1518 :
1519 29 : GNUNET_assert (NULL != instance_id);
1520 29 : GNUNET_assert (NULL != order_id);
1521 29 : if (GNUNET_OK !=
1522 29 : TMH_taler_uri_by_connection (con,
1523 : "pay",
1524 : instance_id,
1525 : &buf))
1526 : {
1527 0 : GNUNET_break (0);
1528 0 : return NULL;
1529 : }
1530 29 : GNUNET_buffer_write_path (&buf,
1531 : order_id);
1532 29 : GNUNET_buffer_write_path (&buf,
1533 : (NULL == session_id)
1534 : ? ""
1535 : : session_id);
1536 29 : if ( (NULL != claim_token) &&
1537 29 : (! GNUNET_is_zero (claim_token)))
1538 : {
1539 : /* Just 'c=' because this goes into QR
1540 : codes, so this is more compact. */
1541 17 : GNUNET_buffer_write_str (&buf,
1542 : "?c=");
1543 17 : GNUNET_buffer_write_data_encoded (&buf,
1544 : (char *) claim_token,
1545 : sizeof (struct TALER_ClaimTokenP));
1546 : }
1547 :
1548 29 : return GNUNET_buffer_reap_str (&buf);
1549 : }
|