Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014-2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero 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.c
18 : * @brief HTTP serving layer intended to perform crypto-work and
19 : * communication with the exchange
20 : * @author Marcello Stanisci
21 : * @author Christian Grothoff
22 : * @author Florian Dold
23 : * @author Priscilla HUANG
24 : */
25 : #include "platform.h"
26 : #include <taler/taler_dbevents.h>
27 : #include <taler/taler_bank_service.h>
28 : #include <taler/taler_mhd_lib.h>
29 : #include <taler/taler_templating_lib.h>
30 : #include <taler/taler_exchange_service.h>
31 : #include "taler_merchant_util.h"
32 : #include "taler-merchant-httpd_config.h"
33 : #include "taler-merchant-httpd_exchanges.h"
34 : #include "taler-merchant-httpd_get-orders-ID.h"
35 : #include "taler-merchant-httpd_get-products-image.h"
36 : #include "taler-merchant-httpd_get-templates-ID.h"
37 : #include "taler-merchant-httpd_helper.h"
38 : #include "taler-merchant-httpd_mhd.h"
39 : #include "taler-merchant-httpd_mfa.h"
40 : #include "taler-merchant-httpd_private-delete-account-ID.h"
41 : #include "taler-merchant-httpd_private-delete-categories-ID.h"
42 : #include "taler-merchant-httpd_private-delete-units-ID.h"
43 : #include "taler-merchant-httpd_private-delete-instances-ID.h"
44 : #include "taler-merchant-httpd_private-delete-instances-ID-token.h"
45 : #include "taler-merchant-httpd_private-delete-products-ID.h"
46 : #include "taler-merchant-httpd_private-delete-orders-ID.h"
47 : #include "taler-merchant-httpd_private-delete-otp-devices-ID.h"
48 : #include "taler-merchant-httpd_private-delete-templates-ID.h"
49 : #include "taler-merchant-httpd_private-delete-token-families-SLUG.h"
50 : #include "taler-merchant-httpd_private-delete-transfers-ID.h"
51 : #include "taler-merchant-httpd_private-delete-webhooks-ID.h"
52 : #include "taler-merchant-httpd_private-get-accounts.h"
53 : #include "taler-merchant-httpd_private-get-accounts-ID.h"
54 : #include "taler-merchant-httpd_private-get-categories.h"
55 : #include "taler-merchant-httpd_private-get-categories-ID.h"
56 : #include "taler-merchant-httpd_private-get-units.h"
57 : #include "taler-merchant-httpd_private-get-units-ID.h"
58 : #include "taler-merchant-httpd_private-get-incoming.h"
59 : #include "taler-merchant-httpd_private-get-instances.h"
60 : #include "taler-merchant-httpd_private-get-instances-ID.h"
61 : #include "taler-merchant-httpd_private-get-instances-ID-kyc.h"
62 : #include "taler-merchant-httpd_private-get-instances-ID-tokens.h"
63 : #include "taler-merchant-httpd_private-get-pos.h"
64 : #include "taler-merchant-httpd_private-get-products.h"
65 : #include "taler-merchant-httpd_private-get-products-ID.h"
66 : #include "taler-merchant-httpd_private-get-orders.h"
67 : #include "taler-merchant-httpd_private-get-orders-ID.h"
68 : #include "taler-merchant-httpd_private-get-otp-devices.h"
69 : #include "taler-merchant-httpd_private-get-otp-devices-ID.h"
70 : #include "taler-merchant-httpd_private-get-statistics-amount-SLUG.h"
71 : #include "taler-merchant-httpd_private-get-statistics-counter-SLUG.h"
72 : #include "taler-merchant-httpd_private-get-templates.h"
73 : #include "taler-merchant-httpd_private-get-templates-ID.h"
74 : #include "taler-merchant-httpd_private-get-token-families.h"
75 : #include "taler-merchant-httpd_private-get-token-families-SLUG.h"
76 : #include "taler-merchant-httpd_private-get-transfers.h"
77 : #include "taler-merchant-httpd_private-get-webhooks.h"
78 : #include "taler-merchant-httpd_private-get-webhooks-ID.h"
79 : #include "taler-merchant-httpd_private-patch-accounts-ID.h"
80 : #include "taler-merchant-httpd_private-patch-categories-ID.h"
81 : #include "taler-merchant-httpd_private-patch-units-ID.h"
82 : #include "taler-merchant-httpd_private-patch-instances-ID.h"
83 : #include "taler-merchant-httpd_private-patch-orders-ID-forget.h"
84 : #include "taler-merchant-httpd_private-patch-otp-devices-ID.h"
85 : #include "taler-merchant-httpd_private-patch-products-ID.h"
86 : #include "taler-merchant-httpd_private-patch-templates-ID.h"
87 : #include "taler-merchant-httpd_private-patch-token-families-SLUG.h"
88 : #include "taler-merchant-httpd_private-patch-webhooks-ID.h"
89 : #include "taler-merchant-httpd_private-post-account.h"
90 : #include "taler-merchant-httpd_private-post-categories.h"
91 : #include "taler-merchant-httpd_private-post-units.h"
92 : #include "taler-merchant-httpd_private-post-instances.h"
93 : #include "taler-merchant-httpd_private-post-instances-ID-auth.h"
94 : #include "taler-merchant-httpd_private-post-instances-ID-token.h"
95 : #include "taler-merchant-httpd_private-post-otp-devices.h"
96 : #include "taler-merchant-httpd_private-post-orders.h"
97 : #include "taler-merchant-httpd_private-post-orders-ID-refund.h"
98 : #include "taler-merchant-httpd_private-post-products.h"
99 : #include "taler-merchant-httpd_private-post-products-ID-lock.h"
100 : #include "taler-merchant-httpd_private-post-templates.h"
101 : #include "taler-merchant-httpd_private-post-token-families.h"
102 : #include "taler-merchant-httpd_private-post-transfers.h"
103 : #include "taler-merchant-httpd_private-post-webhooks.h"
104 : #include "taler-merchant-httpd_post-challenge-ID.h"
105 : #include "taler-merchant-httpd_post-challenge-ID-confirm.h"
106 : #include "taler-merchant-httpd_post-orders-ID-abort.h"
107 : #include "taler-merchant-httpd_post-orders-ID-claim.h"
108 : #include "taler-merchant-httpd_post-orders-ID-paid.h"
109 : #include "taler-merchant-httpd_post-orders-ID-pay.h"
110 : #include "taler-merchant-httpd_post-using-templates.h"
111 : #include "taler-merchant-httpd_post-orders-ID-refund.h"
112 : #include "taler-merchant-httpd_spa.h"
113 : #include "taler-merchant-httpd_statics.h"
114 : #include "taler-merchant-httpd_terms.h"
115 :
116 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
117 : #include "taler-merchant-httpd_private-get-donau-instances.h"
118 : #include "taler-merchant-httpd_private-post-donau-instance.h"
119 : #include "taler-merchant-httpd_private-delete-donau-instance-ID.h"
120 : #endif
121 :
122 : /**
123 : * Backlog for listen operation on unix-domain sockets.
124 : */
125 : #define UNIX_BACKLOG 500
126 :
127 : /**
128 : * Default maximum upload size permitted. Can be overridden
129 : * per handler.
130 : */
131 : #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024)
132 :
133 : char *TMH_currency;
134 :
135 : char *TMH_base_url;
136 :
137 : char *TMH_spa_dir;
138 :
139 : char *TMH_helper_email;
140 :
141 : char *TMH_helper_sms;
142 :
143 : char *TMH_allowed_payment_targets;
144 :
145 : char *TMH_default_persona;
146 :
147 : char *TMH_payment_target_regex;
148 :
149 : regex_t TMH_payment_target_re;
150 :
151 : int TMH_force_audit;
152 :
153 : struct TALER_MERCHANTDB_Plugin *TMH_db;
154 :
155 : struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map;
156 :
157 : struct GNUNET_TIME_Relative TMH_default_pay_delay;
158 :
159 : struct GNUNET_TIME_Relative TMH_default_refund_delay;
160 :
161 : struct GNUNET_TIME_Relative TMH_default_wire_transfer_delay;
162 :
163 : enum GNUNET_TIME_RounderInterval TMH_default_wire_transfer_rounding_interval;
164 :
165 : int TMH_strict_v19;
166 :
167 : int TMH_auth_disabled;
168 :
169 : int TMH_have_self_provisioning;
170 :
171 : enum TEH_TanChannelSet TEH_mandatory_tan_channels;
172 :
173 : struct GNUNET_TIME_Relative TMH_legal_expiration;
174 :
175 : unsigned int TMH_num_cspecs;
176 :
177 : struct TALER_CurrencySpecification *TMH_cspecs;
178 :
179 : struct GNUNET_CURL_Context *TMH_curl_ctx;
180 :
181 : /**
182 : * Event handler for instance settings changes.
183 : */
184 : static struct GNUNET_DB_EventHandler *instance_eh;
185 :
186 : /**
187 : * True if we started any HTTP daemon.
188 : */
189 : static bool have_daemons;
190 :
191 : /**
192 : * Should a "Connection: close" header be added to each HTTP response?
193 : */
194 : static int merchant_connection_close;
195 :
196 : /**
197 : * Context for integrating #TMH_curl_ctx with the
198 : * GNUnet event loop.
199 : */
200 : static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc;
201 :
202 : /**
203 : * Global return code
204 : */
205 : static int global_ret;
206 :
207 : /**
208 : * Our configuration.
209 : */
210 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
211 :
212 : /**
213 : * Maximum length of a permissions string of a scope
214 : */
215 : #define TMH_MAX_SCOPE_PERMISSIONS_LEN 4096
216 :
217 : /**
218 : * Maximum length of a name of a scope
219 : */
220 : #define TMH_MAX_NAME_LEN 255
221 :
222 : /**
223 : * Represents a hard-coded set of default scopes with their
224 : * permissions and names
225 : */
226 : struct ScopePermissionMap
227 : {
228 : /**
229 : * The scope enum value
230 : */
231 : enum TMH_AuthScope as;
232 :
233 : /**
234 : * The scope name
235 : */
236 : char name[TMH_MAX_NAME_LEN];
237 :
238 : /**
239 : * The scope permissions string.
240 : * Comma-separated.
241 : */
242 : char permissions[TMH_MAX_SCOPE_PERMISSIONS_LEN];
243 : };
244 :
245 : /**
246 : * The default scopes array for merchant
247 : */
248 : struct ScopePermissionMap scope_permissions[] = {
249 : /* Deprecated since v19 */
250 : {
251 : .as = TMH_AS_ALL,
252 : .name = "write",
253 : .permissions = "*"
254 : },
255 : /* Full access for SPA */
256 : {
257 : .as = TMH_AS_ALL,
258 : .name = "all",
259 : .permissions = "*"
260 : },
261 : /* Full access for SPA */
262 : {
263 : .as = TMH_AS_SPA,
264 : .name = "spa",
265 : .permissions = "*"
266 : },
267 : /* Read-only access */
268 : {
269 : .as = TMH_AS_READ_ONLY,
270 : .name = "readonly",
271 : .permissions = "*-read"
272 : },
273 : /* Simple order management */
274 : {
275 : .as = TMH_AS_ORDER_SIMPLE,
276 : .name = "order-simple",
277 : .permissions = "orders-read,orders-write"
278 : },
279 : /* Simple order management for PoS, also allows inventory locking */
280 : {
281 : .as = TMH_AS_ORDER_POS,
282 : .name = "order-pos",
283 : .permissions = "orders-read,orders-write,inventory-lock"
284 : },
285 : /* Simple order management, also allows refunding */
286 : {
287 : .as = TMH_AS_ORDER_MGMT,
288 : .name = "order-mgmt",
289 : .permissions = "orders-read,orders-write,orders-refund"
290 : },
291 : /* Full order management, allows inventory locking and refunds */
292 : {
293 : .as = TMH_AS_ORDER_FULL,
294 : .name = "order-full",
295 : .permissions = "orders-read,orders-write,inventory-lock,orders-refund"
296 : },
297 : /* No permissions, dummy scope */
298 : {
299 : .as = TMH_AS_NONE,
300 : }
301 : };
302 :
303 :
304 : /**
305 : * Get permissions string for scope.
306 : * Also extracts the leftmost bit into the @a refreshable
307 : * output parameter.
308 : *
309 : * @param as the scope to get the permissions string from
310 : * @param[out] refreshable true if the token associated with this scope is refreshable.
311 : * @return the permissions string, or NULL if no such scope found
312 : */
313 : static const char*
314 566 : get_scope_permissions (enum TMH_AuthScope as,
315 : bool *refreshable)
316 : {
317 566 : *refreshable = as & TMH_AS_REFRESHABLE;
318 767 : for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
319 : {
320 : /* We ignore the TMH_AS_REFRESHABLE bit */
321 749 : if ( (as & ~TMH_AS_REFRESHABLE) ==
322 749 : (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
323 548 : return scope_permissions[i].permissions;
324 : }
325 18 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
326 : "Failed to find required permissions for scope %d\n",
327 : as);
328 18 : return NULL;
329 : }
330 :
331 :
332 : /**
333 : * Checks if @a permission_required is in permissions of
334 : * @a scope.
335 : *
336 : * @param permission_required the permission to check.
337 : * @param scope the scope to check.
338 : * @return true if @a permission_required is in the permissions set of @a scope.
339 : */
340 : static bool
341 536 : permission_in_scope (const char *permission_required,
342 : enum TMH_AuthScope scope)
343 : {
344 : char *permissions;
345 : const char *perms_tmp;
346 536 : bool is_read_perm = false;
347 536 : bool is_write_perm = false;
348 : bool refreshable;
349 : const char *last_dash;
350 :
351 536 : perms_tmp = get_scope_permissions (scope,
352 : &refreshable);
353 536 : if (NULL == perms_tmp)
354 : {
355 18 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
356 : "Permission check failed: scope %d not understood\n",
357 : (int) scope);
358 18 : return false;
359 : }
360 518 : last_dash = strrchr (permission_required,
361 : '-');
362 518 : if (NULL != last_dash)
363 : {
364 517 : is_write_perm = (0 == strcmp (last_dash,
365 : "-write"));
366 517 : is_read_perm = (0 == strcmp (last_dash,
367 : "-read"));
368 : }
369 :
370 518 : if (0 == strcmp ("token-refresh",
371 : permission_required))
372 : {
373 16 : if (! refreshable)
374 : {
375 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
376 : "Permission check failed: token not refreshable\n");
377 : }
378 16 : return refreshable;
379 : }
380 502 : permissions = GNUNET_strdup (perms_tmp);
381 : {
382 502 : const char *perm = strtok (permissions,
383 : ",");
384 :
385 502 : if (NULL == perm)
386 : {
387 0 : GNUNET_free (permissions);
388 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
389 : "Permission check failed: empty permission set\n");
390 0 : return false;
391 : }
392 503 : while (NULL != perm)
393 : {
394 502 : if (0 == strcmp ("*",
395 : perm))
396 : {
397 500 : GNUNET_free (permissions);
398 500 : return true;
399 : }
400 2 : if ( (0 == strcmp ("*-write",
401 0 : perm)) &&
402 : (is_write_perm) )
403 : {
404 0 : GNUNET_free (permissions);
405 0 : return true;
406 : }
407 2 : if ( (0 == strcmp ("*-read",
408 2 : perm)) &&
409 : (is_read_perm) )
410 : {
411 1 : GNUNET_free (permissions);
412 1 : return true;
413 : }
414 1 : if (0 == strcmp (permission_required,
415 : perm))
416 : {
417 0 : GNUNET_free (permissions);
418 0 : return true;
419 : }
420 1 : perm = strtok (NULL,
421 : ",");
422 : }
423 : }
424 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
425 : "Permission check failed: %s not found in %s\n",
426 : permission_required,
427 : permissions);
428 1 : GNUNET_free (permissions);
429 1 : return false;
430 : }
431 :
432 :
433 : bool
434 15 : TMH_scope_is_subset (enum TMH_AuthScope as,
435 : enum TMH_AuthScope candidate)
436 : {
437 : const char *as_perms;
438 : const char *candidate_perms;
439 : char *permissions;
440 : bool as_refreshable;
441 : bool cand_refreshable;
442 :
443 15 : as_perms = get_scope_permissions (as,
444 : &as_refreshable);
445 15 : candidate_perms = get_scope_permissions (candidate,
446 : &cand_refreshable);
447 15 : if (! as_refreshable && cand_refreshable)
448 0 : return false;
449 15 : if ( (NULL == as_perms) &&
450 : (NULL != candidate_perms) )
451 0 : return false;
452 15 : if ( (NULL == candidate_perms) ||
453 15 : (0 == strcmp ("*",
454 : as_perms)))
455 14 : return true;
456 1 : permissions = GNUNET_strdup (candidate_perms);
457 : {
458 : const char *perm;
459 :
460 1 : perm = strtok (permissions,
461 : ",");
462 1 : if (NULL == perm)
463 : {
464 0 : GNUNET_free (permissions);
465 0 : return true;
466 : }
467 1 : while (NULL != perm)
468 : {
469 1 : if (! permission_in_scope (perm,
470 : as))
471 : {
472 1 : GNUNET_free (permissions);
473 1 : return false;
474 : }
475 0 : perm = strtok (NULL,
476 : ",");
477 : }
478 : }
479 0 : GNUNET_free (permissions);
480 0 : return true;
481 : }
482 :
483 :
484 : enum TMH_AuthScope
485 15 : TMH_get_scope_by_name (const char *name)
486 : {
487 36 : for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
488 : {
489 36 : if (0 == strcasecmp (scope_permissions[i].name,
490 : name))
491 15 : return scope_permissions[i].as;
492 : }
493 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
494 : "Name `%s' does not match any scope we understand\n",
495 : name);
496 0 : return TMH_AS_NONE;
497 : }
498 :
499 :
500 : const char*
501 2 : TMH_get_name_by_scope (enum TMH_AuthScope scope,
502 : bool *refreshable)
503 : {
504 2 : *refreshable = scope & TMH_AS_REFRESHABLE;
505 8 : for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
506 : {
507 : /* We ignore the TMH_AS_REFRESHABLE bit */
508 8 : if ( (scope & ~TMH_AS_REFRESHABLE) ==
509 8 : (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
510 2 : return scope_permissions[i].name;
511 : }
512 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
513 : "Scope #%d does not match any scope we understand\n",
514 : (int) scope);
515 0 : return NULL;
516 : }
517 :
518 :
519 : enum GNUNET_GenericReturnValue
520 23 : TMH_check_auth (const char *password,
521 : struct TALER_MerchantAuthenticationSaltP *salt,
522 : struct TALER_MerchantAuthenticationHashP *hash)
523 : {
524 : struct TALER_MerchantAuthenticationHashP val;
525 :
526 23 : if (GNUNET_is_zero (hash))
527 0 : return GNUNET_OK;
528 23 : if (NULL == password)
529 : {
530 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
531 : "Denying access: empty password provided\n");
532 0 : return GNUNET_SYSERR;
533 : }
534 23 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535 : "Checking against token with salt %s\n",
536 : TALER_B2S (salt));
537 23 : TALER_merchant_instance_auth_hash_with_salt (&val,
538 : salt,
539 : password);
540 23 : if (0 !=
541 23 : GNUNET_memcmp (&val,
542 : hash))
543 : {
544 10 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
545 : "Access denied: password does not match\n");
546 10 : return GNUNET_SYSERR;
547 : }
548 13 : return GNUNET_OK;
549 : }
550 :
551 :
552 : /**
553 : * Check if @a userpass grants access to @a instance.
554 : *
555 : * @param userpass base64 encoded "$USERNAME:$PASSWORD" value
556 : * from HTTP Basic "Authentication" header
557 : * @param instance the access controlled instance
558 : */
559 : static enum GNUNET_GenericReturnValue
560 14 : check_auth_instance (const char *userpass,
561 : struct TMH_MerchantInstance *instance)
562 : {
563 : char *tmp;
564 : char *colon;
565 : const char *instance_name;
566 : const char *password;
567 14 : const char *target_instance = "admin";
568 : enum GNUNET_GenericReturnValue ret;
569 :
570 : /* implicitly a zeroed out hash means no authentication */
571 14 : if (GNUNET_is_zero (&instance->auth.auth_hash))
572 0 : return GNUNET_OK;
573 14 : if (NULL == userpass)
574 : {
575 0 : GNUNET_break_op (0);
576 0 : return GNUNET_SYSERR;
577 : }
578 14 : if (0 ==
579 14 : GNUNET_STRINGS_base64_decode (userpass,
580 : strlen (userpass),
581 : (void**) &tmp))
582 : {
583 0 : GNUNET_break_op (0);
584 0 : return GNUNET_SYSERR;
585 : }
586 14 : colon = strchr (tmp,
587 : ':');
588 14 : if (NULL == colon)
589 : {
590 0 : GNUNET_break_op (0);
591 0 : GNUNET_free (tmp);
592 0 : return GNUNET_SYSERR;
593 : }
594 14 : *colon = '\0';
595 14 : instance_name = tmp;
596 14 : password = colon + 1;
597 : /* instance->settings.id can be NULL if there is no instance yet */
598 14 : if (NULL != instance->settings.id)
599 14 : target_instance = instance->settings.id;
600 14 : if (0 != strcmp (instance_name,
601 : target_instance))
602 : {
603 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
604 : "Somebody tried to login to instance %s with username %s (login failed).\n",
605 : target_instance,
606 : instance_name);
607 0 : GNUNET_free (tmp);
608 0 : return GNUNET_SYSERR;
609 : }
610 14 : ret = TMH_check_auth (password,
611 : &instance->auth.auth_salt,
612 : &instance->auth.auth_hash);
613 14 : GNUNET_free (tmp);
614 14 : if (GNUNET_OK != ret)
615 : {
616 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
617 : "Password provided does not match credentials for %s\n",
618 : target_instance);
619 : }
620 14 : return ret;
621 : }
622 :
623 :
624 : void
625 13 : TMH_compute_auth (const char *token,
626 : struct TALER_MerchantAuthenticationSaltP *salt,
627 : struct TALER_MerchantAuthenticationHashP *hash)
628 : {
629 13 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
630 : salt,
631 : sizeof (*salt));
632 13 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
633 : "Computing initial auth using token with salt %s\n",
634 : TALER_B2S (salt));
635 13 : TALER_merchant_instance_auth_hash_with_salt (hash,
636 : salt,
637 : token);
638 13 : }
639 :
640 :
641 : void
642 54 : TMH_wire_method_free (struct TMH_WireMethod *wm)
643 : {
644 54 : GNUNET_free (wm->payto_uri.full_payto);
645 54 : GNUNET_free (wm->wire_method);
646 54 : GNUNET_free (wm->credit_facade_url);
647 54 : json_decref (wm->credit_facade_credentials);
648 54 : GNUNET_free (wm);
649 54 : }
650 :
651 :
652 : void
653 807 : TMH_instance_decref (struct TMH_MerchantInstance *mi)
654 : {
655 : struct TMH_WireMethod *wm;
656 :
657 807 : mi->rc--;
658 807 : if (0 != mi->rc)
659 705 : return;
660 102 : TMH_force_get_orders_resume (mi);
661 156 : while (NULL != (wm = (mi->wm_head)))
662 : {
663 54 : GNUNET_CONTAINER_DLL_remove (mi->wm_head,
664 : mi->wm_tail,
665 : wm);
666 54 : TMH_wire_method_free (wm);
667 : }
668 :
669 102 : GNUNET_free (mi->settings.id);
670 102 : GNUNET_free (mi->settings.name);
671 102 : GNUNET_free (mi->settings.email);
672 102 : GNUNET_free (mi->settings.phone);
673 102 : GNUNET_free (mi->settings.website);
674 102 : GNUNET_free (mi->settings.logo);
675 102 : json_decref (mi->settings.address);
676 102 : json_decref (mi->settings.jurisdiction);
677 102 : GNUNET_free (mi);
678 : }
679 :
680 :
681 : enum GNUNET_GenericReturnValue
682 102 : TMH_instance_free_cb (void *cls,
683 : const struct GNUNET_HashCode *key,
684 : void *value)
685 : {
686 102 : struct TMH_MerchantInstance *mi = value;
687 :
688 : (void) cls;
689 : (void) key;
690 102 : GNUNET_assert (GNUNET_OK ==
691 : GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map,
692 : &mi->h_instance,
693 : mi));
694 102 : TMH_instance_decref (mi);
695 102 : return GNUNET_YES;
696 : }
697 :
698 :
699 : /**
700 : * Shutdown task (invoked when the application is being
701 : * terminated for any reason)
702 : *
703 : * @param cls NULL
704 : */
705 : static void
706 15 : do_shutdown (void *cls)
707 : {
708 : (void) cls;
709 15 : TALER_MHD_daemons_halt ();
710 15 : TMH_force_orders_resume ();
711 15 : TMH_force_ac_resume ();
712 15 : TMH_force_pc_resume ();
713 15 : TMH_force_kyc_resume ();
714 15 : TMH_force_gorc_resume ();
715 15 : TMH_force_wallet_get_order_resume ();
716 15 : TMH_force_wallet_refund_order_resume ();
717 15 : TMH_challenge_done ();
718 15 : TALER_MHD_daemons_destroy ();
719 15 : if (NULL != instance_eh)
720 : {
721 15 : TMH_db->event_listen_cancel (instance_eh);
722 15 : instance_eh = NULL;
723 : }
724 15 : TMH_EXCHANGES_done ();
725 15 : if (NULL != TMH_db)
726 : {
727 15 : TALER_MERCHANTDB_plugin_unload (TMH_db);
728 15 : TMH_db = NULL;
729 : }
730 15 : if (NULL != TMH_by_id_map)
731 : {
732 15 : GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
733 : &TMH_instance_free_cb,
734 : NULL);
735 15 : GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map);
736 15 : TMH_by_id_map = NULL;
737 : }
738 15 : TALER_TEMPLATING_done ();
739 15 : if (NULL != TMH_curl_ctx)
740 : {
741 15 : GNUNET_CURL_fini (TMH_curl_ctx);
742 15 : TMH_curl_ctx = NULL;
743 : }
744 15 : if (NULL != merchant_curl_rc)
745 : {
746 15 : GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
747 15 : merchant_curl_rc = NULL;
748 : }
749 15 : if (NULL != TMH_payment_target_regex)
750 : {
751 0 : regfree (&TMH_payment_target_re);
752 0 : GNUNET_free (TMH_payment_target_regex);
753 : }
754 15 : }
755 :
756 :
757 : /**
758 : * Function called whenever MHD is done with a request. If the
759 : * request was a POST, we may have stored a `struct Buffer *` in the
760 : * @a con_cls that might still need to be cleaned up. Call the
761 : * respective function to free the memory.
762 : *
763 : * @param cls client-defined closure
764 : * @param connection connection handle
765 : * @param con_cls value as set by the last call to
766 : * the #MHD_AccessHandlerCallback
767 : * @param toe reason for request termination
768 : * @see #MHD_OPTION_NOTIFY_COMPLETED
769 : * @ingroup request
770 : */
771 : static void
772 749 : handle_mhd_completion_callback (void *cls,
773 : struct MHD_Connection *connection,
774 : void **con_cls,
775 : enum MHD_RequestTerminationCode toe)
776 : {
777 749 : struct TMH_HandlerContext *hc = *con_cls;
778 :
779 : (void) cls;
780 749 : if (NULL == hc)
781 0 : return;
782 749 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
783 : {
784 : #if MHD_VERSION >= 0x00097304
785 : const union MHD_ConnectionInfo *ci;
786 749 : unsigned int http_status = 0;
787 :
788 749 : ci = MHD_get_connection_info (connection,
789 : MHD_CONNECTION_INFO_HTTP_STATUS);
790 749 : if (NULL != ci)
791 749 : http_status = ci->http_status;
792 749 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
793 : "Request for `%s' completed with HTTP status %u (%d)\n",
794 : hc->url,
795 : http_status,
796 : toe);
797 : #else
798 : (void) connection;
799 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
800 : "Finished handling request for `%s' with MHD termination code %d\n",
801 : hc->url,
802 : (int) toe);
803 : #endif
804 : }
805 749 : if (NULL != hc->cc)
806 256 : hc->cc (hc->ctx);
807 749 : TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
808 749 : GNUNET_free (hc->infix);
809 749 : if (NULL != hc->request_body)
810 424 : json_decref (hc->request_body);
811 749 : if (NULL != hc->instance)
812 705 : TMH_instance_decref (hc->instance);
813 749 : TMH_db->preflight (TMH_db->cls);
814 749 : GNUNET_free (hc->full_url);
815 749 : GNUNET_free (hc);
816 749 : *con_cls = NULL;
817 : }
818 :
819 :
820 : struct TMH_MerchantInstance *
821 1014 : TMH_lookup_instance (const char *instance_id)
822 : {
823 : struct GNUNET_HashCode h_instance;
824 :
825 1014 : if (NULL == instance_id)
826 666 : instance_id = "admin";
827 1014 : GNUNET_CRYPTO_hash (instance_id,
828 : strlen (instance_id),
829 : &h_instance);
830 1014 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
831 : "Looking for by-id key %s of '%s' in hashmap\n",
832 : GNUNET_h2s (&h_instance),
833 : instance_id);
834 : /* We're fine if that returns NULL, the calling routine knows how
835 : to handle that */
836 1014 : return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map,
837 : &h_instance);
838 : }
839 :
840 :
841 : /**
842 : * Add instance definition to our active set of instances.
843 : *
844 : * @param[in,out] mi merchant instance details to define
845 : * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already
846 : */
847 : enum GNUNET_GenericReturnValue
848 102 : TMH_add_instance (struct TMH_MerchantInstance *mi)
849 : {
850 : const char *id;
851 : int ret;
852 :
853 102 : id = mi->settings.id;
854 102 : if (NULL == id)
855 0 : id = "admin";
856 102 : GNUNET_CRYPTO_hash (id,
857 : strlen (id),
858 : &mi->h_instance);
859 102 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
860 : "Looking for by-id key %s of `%s' in hashmap\n",
861 : GNUNET_h2s (&mi->h_instance),
862 : id);
863 102 : ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map,
864 102 : &mi->h_instance,
865 : mi,
866 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
867 102 : if (GNUNET_OK == ret)
868 : {
869 102 : GNUNET_assert (mi->rc < UINT_MAX);
870 102 : mi->rc++;
871 : }
872 102 : return ret;
873 : }
874 :
875 :
876 : /**
877 : * Handle a OPTIONS "*" request.
878 : *
879 : * @param rh context of the handler
880 : * @param connection the MHD connection to handle
881 : * @param[in,out] hc context with further information about the request
882 : * @return MHD result code
883 : */
884 : static MHD_RESULT
885 0 : handle_server_options (const struct TMH_RequestHandler *rh,
886 : struct MHD_Connection *connection,
887 : struct TMH_HandlerContext *hc)
888 : {
889 : (void) rh;
890 : (void) hc;
891 0 : return TALER_MHD_reply_cors_preflight (connection);
892 : }
893 :
894 :
895 : /**
896 : * Generates the response for "/", redirecting the
897 : * client to the "/webui/" from where we serve the SPA.
898 : *
899 : * @param rh request handler
900 : * @param connection MHD connection
901 : * @param hc handler context
902 : * @return MHD result code
903 : */
904 : static MHD_RESULT
905 1 : spa_redirect (const struct TMH_RequestHandler *rh,
906 : struct MHD_Connection *connection,
907 : struct TMH_HandlerContext *hc)
908 : {
909 1 : const char *text = "Redirecting to /webui/";
910 : struct MHD_Response *response;
911 : char *dst;
912 :
913 1 : response = MHD_create_response_from_buffer (strlen (text),
914 : (void *) text,
915 : MHD_RESPMEM_PERSISTENT);
916 1 : if (NULL == response)
917 : {
918 0 : GNUNET_break (0);
919 0 : return MHD_NO;
920 : }
921 1 : TALER_MHD_add_global_headers (response,
922 : true);
923 1 : GNUNET_break (MHD_YES ==
924 : MHD_add_response_header (response,
925 : MHD_HTTP_HEADER_CONTENT_TYPE,
926 : "text/plain"));
927 1 : if ( (NULL == hc->instance) ||
928 1 : (0 == strcmp ("admin",
929 1 : hc->instance->settings.id)) )
930 1 : dst = GNUNET_strdup ("/webui/");
931 : else
932 0 : GNUNET_asprintf (&dst,
933 : "/instances/%s/webui/",
934 0 : hc->instance->settings.id);
935 1 : if (MHD_NO ==
936 1 : MHD_add_response_header (response,
937 : MHD_HTTP_HEADER_LOCATION,
938 : dst))
939 : {
940 0 : GNUNET_break (0);
941 0 : MHD_destroy_response (response);
942 0 : GNUNET_free (dst);
943 0 : return MHD_NO;
944 : }
945 1 : GNUNET_free (dst);
946 :
947 : {
948 : MHD_RESULT ret;
949 :
950 1 : ret = MHD_queue_response (connection,
951 : MHD_HTTP_FOUND,
952 : response);
953 1 : MHD_destroy_response (response);
954 1 : return ret;
955 : }
956 : }
957 :
958 :
959 : /**
960 : * Extract the token from authorization header value @a auth.
961 : * The @a auth value can be a bearer token or a Basic
962 : * authentication header. In both cases, this function
963 : * updates @a auth to point to the actual credential,
964 : * skipping spaces.
965 : *
966 : * NOTE: We probably want to replace this function with MHD2
967 : * API calls in the future that are more robust.
968 : *
969 : * @param[in,out] auth pointer to authorization header value,
970 : * will be updated to point to the start of the token
971 : * or set to NULL if header value is invalid
972 : * @param[out] is_basic_auth will be set to true if the
973 : * authorization header uses basic authentication,
974 : * otherwise to false
975 : */
976 : static void
977 71 : extract_auth (const char **auth,
978 : bool *is_basic_auth)
979 : {
980 71 : const char *bearer = "Bearer ";
981 71 : const char *basic = "Basic ";
982 71 : const char *tok = *auth;
983 71 : size_t offset = 0;
984 71 : bool is_bearer = false;
985 :
986 71 : *is_basic_auth = false;
987 71 : if (0 == strncmp (tok,
988 : bearer,
989 : strlen (bearer)))
990 : {
991 57 : offset = strlen (bearer);
992 57 : is_bearer = true;
993 : }
994 14 : else if (0 == strncmp (tok,
995 : basic,
996 : strlen (basic)))
997 : {
998 14 : offset = strlen (basic);
999 14 : *is_basic_auth = true;
1000 : }
1001 : else
1002 : {
1003 0 : *auth = NULL;
1004 0 : return;
1005 : }
1006 71 : tok += offset;
1007 71 : while (' ' == *tok)
1008 0 : tok++;
1009 71 : if ( (is_bearer) &&
1010 57 : (0 != strncasecmp (tok,
1011 : RFC_8959_PREFIX,
1012 : strlen (RFC_8959_PREFIX))) )
1013 : {
1014 0 : *auth = NULL;
1015 0 : return;
1016 : }
1017 71 : *auth = tok;
1018 : }
1019 :
1020 :
1021 : /**
1022 : * Checks if the @a rh matches the given (parsed) URL.
1023 : *
1024 : * @param rh handler to compare against
1025 : * @param url the main URL (without "/private/" prefix, if any)
1026 : * @param prefix_strlen length of the prefix, i.e. 8 for '/orders/' or 7 for '/config'
1027 : * @param infix_url infix text, i.e. "$ORDER_ID".
1028 : * @param infix_strlen length of the string in @a infix_url
1029 : * @param suffix_url suffix, i.e. "/refund", including the "/"
1030 : * @param suffix_strlen number of characters in @a suffix_url
1031 : * @return true if @a rh matches this request
1032 : */
1033 : static bool
1034 15790 : prefix_match (const struct TMH_RequestHandler *rh,
1035 : const char *url,
1036 : size_t prefix_strlen,
1037 : const char *infix_url,
1038 : size_t infix_strlen,
1039 : const char *suffix_url,
1040 : size_t suffix_strlen)
1041 : {
1042 15790 : if ( (prefix_strlen != strlen (rh->url_prefix)) ||
1043 2447 : (0 != memcmp (url,
1044 2447 : rh->url_prefix,
1045 : prefix_strlen)) )
1046 14353 : return false;
1047 1437 : if (! rh->have_id_segment)
1048 : {
1049 : /* Require /$PREFIX/$SUFFIX or /$PREFIX */
1050 456 : if (NULL != suffix_url)
1051 0 : return false; /* too many segments to match */
1052 456 : if ( (NULL == infix_url) /* either or */
1053 456 : ^ (NULL == rh->url_suffix) )
1054 0 : return false; /* suffix existence mismatch */
1055 : /* If /$PREFIX/$SUFFIX, check $SUFFIX matches */
1056 456 : if ( (NULL != infix_url) &&
1057 0 : ( (infix_strlen != strlen (rh->url_suffix)) ||
1058 0 : (0 != memcmp (infix_url,
1059 0 : rh->url_suffix,
1060 : infix_strlen)) ) )
1061 0 : return false; /* cannot use infix as suffix: content mismatch */
1062 : }
1063 : else
1064 : {
1065 : /* Require /$PREFIX/$ID or /$PREFIX/$ID/$SUFFIX */
1066 981 : if (NULL == infix_url)
1067 0 : return false; /* infix existence mismatch */
1068 981 : if ( ( (NULL == suffix_url)
1069 981 : ^ (NULL == rh->url_suffix) ) )
1070 253 : return false; /* suffix existence mismatch */
1071 728 : if ( (NULL != suffix_url) &&
1072 363 : ( (suffix_strlen != strlen (rh->url_suffix)) ||
1073 253 : (0 != memcmp (suffix_url,
1074 253 : rh->url_suffix,
1075 : suffix_strlen)) ) )
1076 192 : return false; /* suffix content mismatch */
1077 : }
1078 992 : return true;
1079 : }
1080 :
1081 :
1082 : /**
1083 : * Function called first by MHD with the full URL.
1084 : *
1085 : * @param cls NULL
1086 : * @param full_url the full URL
1087 : * @param con MHD connection object
1088 : * @return our handler context
1089 : */
1090 : static void *
1091 749 : full_url_track_callback (void *cls,
1092 : const char *full_url,
1093 : struct MHD_Connection *con)
1094 : {
1095 : struct TMH_HandlerContext *hc;
1096 :
1097 749 : hc = GNUNET_new (struct TMH_HandlerContext);
1098 749 : hc->connection = con;
1099 749 : GNUNET_async_scope_fresh (&hc->async_scope_id);
1100 749 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
1101 749 : hc->full_url = GNUNET_strdup (full_url);
1102 749 : return hc;
1103 : }
1104 :
1105 :
1106 : /**
1107 : * Function used to process Basic authorization header value.
1108 : * Sets correct scope in the auth_scope parameter of the
1109 : * #TMH_HandlerContext.
1110 : *
1111 : * @param hc the handler context
1112 : * @param authn_s the value of the authorization header
1113 : */
1114 : static void
1115 14 : process_basic_auth (struct TMH_HandlerContext *hc,
1116 : const char *authn_s)
1117 : {
1118 : /* Handle token endpoint slightly differently: Only allow
1119 : * instance password (Basic auth) to retrieve access token.
1120 : * We need to handle authorization with Basic auth here first
1121 : * The only time we need to handle authentication like this is
1122 : * for the token endpoint!
1123 : */
1124 14 : if ( (0 != strncmp (hc->rh->url_prefix,
1125 : "/token",
1126 14 : strlen ("/token"))) ||
1127 14 : (0 != strncmp (MHD_HTTP_METHOD_POST,
1128 14 : hc->rh->method,
1129 14 : strlen (MHD_HTTP_METHOD_POST))) ||
1130 14 : (NULL == hc->instance))
1131 : {
1132 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1133 : "Called endpoint `%s' with Basic authentication. Rejecting...\n",
1134 : hc->rh->url_prefix);
1135 0 : hc->auth_scope = TMH_AS_NONE;
1136 0 : return;
1137 : }
1138 14 : if (GNUNET_OK ==
1139 14 : check_auth_instance (authn_s,
1140 : hc->instance))
1141 : {
1142 13 : hc->auth_scope = TMH_AS_ALL;
1143 : }
1144 : else
1145 : {
1146 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1147 : "Basic authentication failed!\n");
1148 1 : hc->auth_scope = TMH_AS_NONE;
1149 : }
1150 : }
1151 :
1152 :
1153 : /**
1154 : * Function used to process Bearer authorization header value.
1155 : * Sets correct scope in the auth_scope parameter of the
1156 : * #TMH_HandlerContext..
1157 : *
1158 : * @param hc the handler context
1159 : * @param authn_s the value of the authorization header
1160 : * @return TALER_EC_NONE on success.
1161 : */
1162 : static enum TALER_ErrorCode
1163 513 : process_bearer_auth (struct TMH_HandlerContext *hc,
1164 : const char *authn_s)
1165 : {
1166 513 : if (NULL == hc->instance)
1167 : {
1168 2 : hc->auth_scope = TMH_AS_NONE;
1169 2 : return TALER_EC_NONE;
1170 : }
1171 511 : if (GNUNET_is_zero (&hc->instance->auth.auth_hash))
1172 : {
1173 : /* hash zero means no authentication for instance */
1174 468 : hc->auth_scope = TMH_AS_ALL;
1175 468 : return TALER_EC_NONE;
1176 : }
1177 : {
1178 : enum TALER_ErrorCode ec;
1179 :
1180 43 : ec = TMH_check_token (authn_s,
1181 43 : hc->instance->settings.id,
1182 : &hc->auth_scope);
1183 43 : if (TALER_EC_NONE != ec)
1184 : {
1185 : char *dec;
1186 : size_t dec_len;
1187 : const char *token;
1188 :
1189 : /* NOTE: Deprecated, remove sometime after v1.1 */
1190 9 : if (0 != strncasecmp (authn_s,
1191 : RFC_8959_PREFIX,
1192 : strlen (RFC_8959_PREFIX)))
1193 : {
1194 0 : GNUNET_break_op (0);
1195 0 : hc->auth_scope = TMH_AS_NONE;
1196 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1197 : "Authentication token invalid: %d\n",
1198 : (int) ec);
1199 9 : return ec;
1200 : }
1201 9 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1202 : "Trying deprecated secret-token:password API authN\n");
1203 9 : token = authn_s + strlen (RFC_8959_PREFIX);
1204 9 : dec_len = GNUNET_STRINGS_urldecode (token,
1205 : strlen (token),
1206 : &dec);
1207 18 : if ( (0 == dec_len) ||
1208 : (GNUNET_OK !=
1209 9 : TMH_check_auth (dec,
1210 9 : &hc->instance->auth.auth_salt,
1211 9 : &hc->instance->auth.auth_hash)) )
1212 : {
1213 9 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1214 : "Login failed\n");
1215 9 : hc->auth_scope = TMH_AS_NONE;
1216 9 : GNUNET_free (dec);
1217 9 : return TALER_EC_NONE;
1218 : }
1219 0 : hc->auth_scope = TMH_AS_ALL;
1220 0 : GNUNET_free (dec);
1221 : }
1222 : }
1223 34 : return TALER_EC_NONE;
1224 : }
1225 :
1226 :
1227 : /**
1228 : * A client has requested the given url using the given method
1229 : * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
1230 : * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
1231 : * must call MHD callbacks to provide content to give back to the
1232 : * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
1233 : * #MHD_HTTP_NOT_FOUND, etc.).
1234 : *
1235 : * @param cls argument given together with the function
1236 : * pointer when the handler was registered with MHD
1237 : * @param connection the MHD connection to handle
1238 : * @param url the requested url
1239 : * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
1240 : * #MHD_HTTP_METHOD_PUT, etc.)
1241 : * @param version the HTTP version string (i.e.
1242 : * #MHD_HTTP_VERSION_1_1)
1243 : * @param upload_data the data being uploaded (excluding HEADERS,
1244 : * for a POST that fits into memory and that is encoded
1245 : * with a supported encoding, the POST data will NOT be
1246 : * given in upload_data and is instead available as
1247 : * part of #MHD_get_connection_values; very large POST
1248 : * data *will* be made available incrementally in
1249 : * @a upload_data)
1250 : * @param upload_data_size set initially to the size of the
1251 : * @a upload_data provided; the method must update this
1252 : * value to the number of bytes NOT processed;
1253 : * @param con_cls pointer that the callback can set to some
1254 : * address and that will be preserved by MHD for future
1255 : * calls for this request; since the access handler may
1256 : * be called many times (i.e., for a PUT/POST operation
1257 : * with plenty of upload data) this allows the application
1258 : * to easily associate some request-specific state.
1259 : * If necessary, this state can be cleaned up in the
1260 : * global #MHD_RequestCompletedCallback (which
1261 : * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
1262 : * Initially, `*con_cls` will be set up by the
1263 : * full_url_track_callback().
1264 : * @return #MHD_YES if the connection was handled successfully,
1265 : * #MHD_NO if the socket must be closed due to a serious
1266 : * error while handling the request
1267 : */
1268 : static MHD_RESULT
1269 2018 : url_handler (void *cls,
1270 : struct MHD_Connection *connection,
1271 : const char *url,
1272 : const char *method,
1273 : const char *version,
1274 : const char *upload_data,
1275 : size_t *upload_data_size,
1276 : void **con_cls)
1277 : {
1278 : static struct TMH_RequestHandler management_handlers[] = {
1279 : /* GET /instances */
1280 : {
1281 : .url_prefix = "/instances",
1282 : .method = MHD_HTTP_METHOD_GET,
1283 : .permission = "instances-write",
1284 : .skip_instance = true,
1285 : .default_only = true,
1286 : .handler = &TMH_private_get_instances
1287 : },
1288 : /* POST /instances */
1289 : {
1290 : .url_prefix = "/instances",
1291 : .method = MHD_HTTP_METHOD_POST,
1292 : .permission = "instances-write",
1293 : .skip_instance = true,
1294 : .default_only = true,
1295 : .handler = &TMH_private_post_instances,
1296 : /* allow instance data of up to 8 MB, that should be plenty;
1297 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1298 : would require further changes to the allocation logic
1299 : in the code... */
1300 : .max_upload = 1024 * 1024 * 8
1301 : },
1302 : /* GET /instances/$ID/ */
1303 : {
1304 : .url_prefix = "/instances/",
1305 : .method = MHD_HTTP_METHOD_GET,
1306 : .permission = "instances-write",
1307 : .skip_instance = true,
1308 : .default_only = true,
1309 : .have_id_segment = true,
1310 : .handler = &TMH_private_get_instances_default_ID
1311 : },
1312 : /* DELETE /instances/$ID */
1313 : {
1314 : .url_prefix = "/instances/",
1315 : .method = MHD_HTTP_METHOD_DELETE,
1316 : .permission = "instances-write",
1317 : .skip_instance = true,
1318 : .default_only = true,
1319 : .have_id_segment = true,
1320 : .handler = &TMH_private_delete_instances_default_ID
1321 : },
1322 : /* PATCH /instances/$ID */
1323 : {
1324 : .url_prefix = "/instances/",
1325 : .method = MHD_HTTP_METHOD_PATCH,
1326 : .permission = "instances-write",
1327 : .skip_instance = true,
1328 : .default_only = true,
1329 : .have_id_segment = true,
1330 : .handler = &TMH_private_patch_instances_default_ID,
1331 : /* allow instance data of up to 8 MB, that should be plenty;
1332 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1333 : would require further changes to the allocation logic
1334 : in the code... */
1335 : .max_upload = 1024 * 1024 * 8
1336 : },
1337 : /* POST /auth: */
1338 : {
1339 : .url_prefix = "/instances/",
1340 : .url_suffix = "auth",
1341 : .method = MHD_HTTP_METHOD_POST,
1342 : .permission = "instances-auth-write",
1343 : .skip_instance = true,
1344 : .default_only = true,
1345 : .have_id_segment = true,
1346 : .handler = &TMH_private_post_instances_default_ID_auth,
1347 : /* Body should be pretty small. */
1348 : .max_upload = 1024 * 1024
1349 : },
1350 : /* GET /kyc: */
1351 : {
1352 : .url_prefix = "/instances/",
1353 : .url_suffix = "kyc",
1354 : .method = MHD_HTTP_METHOD_GET,
1355 : .permission = "instances-kyc-read",
1356 : .skip_instance = true,
1357 : .default_only = true,
1358 : .have_id_segment = true,
1359 : .handler = &TMH_private_get_instances_default_ID_kyc,
1360 : },
1361 : {
1362 : .url_prefix = NULL
1363 : }
1364 : };
1365 :
1366 : static struct TMH_RequestHandler private_handlers[] = {
1367 : /* GET /instances/$ID/: */
1368 : {
1369 : .url_prefix = "/",
1370 : .method = MHD_HTTP_METHOD_GET,
1371 : .permission = "instances-read",
1372 : .handler = &TMH_private_get_instances_ID
1373 : },
1374 : /* DELETE /instances/$ID/: */
1375 : {
1376 : .url_prefix = "/",
1377 : .method = MHD_HTTP_METHOD_DELETE,
1378 : .permission = "instances-write",
1379 : .allow_deleted_instance = true,
1380 : .handler = &TMH_private_delete_instances_ID
1381 : },
1382 : /* PATCH /instances/$ID/: */
1383 : {
1384 : .url_prefix = "/",
1385 : .method = MHD_HTTP_METHOD_PATCH,
1386 : .handler = &TMH_private_patch_instances_ID,
1387 : .permission = "instances-write",
1388 : .allow_deleted_instance = true,
1389 : /* allow instance data of up to 8 MB, that should be plenty;
1390 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1391 : would require further changes to the allocation logic
1392 : in the code... */
1393 : .max_upload = 1024 * 1024 * 8
1394 : },
1395 : /* POST /auth: */
1396 : {
1397 : .url_prefix = "/auth",
1398 : .method = MHD_HTTP_METHOD_POST,
1399 : .handler = &TMH_private_post_instances_ID_auth,
1400 : .permission = "auth-write",
1401 : /* Body should be pretty small. */
1402 : .max_upload = 1024 * 1024,
1403 : },
1404 : /* GET /kyc: */
1405 : {
1406 : .url_prefix = "/kyc",
1407 : .method = MHD_HTTP_METHOD_GET,
1408 : .permission = "kyc-read",
1409 : .handler = &TMH_private_get_instances_ID_kyc,
1410 : },
1411 : /* GET /pos: */
1412 : {
1413 : .url_prefix = "/pos",
1414 : .method = MHD_HTTP_METHOD_GET,
1415 : .permission = "pos-read",
1416 : .handler = &TMH_private_get_pos
1417 : },
1418 : /* GET /categories: */
1419 : {
1420 : .url_prefix = "/categories",
1421 : .method = MHD_HTTP_METHOD_GET,
1422 : .permission = "categories-read",
1423 : .handler = &TMH_private_get_categories
1424 : },
1425 : /* POST /categories: */
1426 : {
1427 : .url_prefix = "/categories",
1428 : .method = MHD_HTTP_METHOD_POST,
1429 : .permission = "categories-write",
1430 : .handler = &TMH_private_post_categories,
1431 : /* allow category data of up to 8 kb, that should be plenty */
1432 : .max_upload = 1024 * 8
1433 : },
1434 : /* GET /categories/$ID: */
1435 : {
1436 : .url_prefix = "/categories/",
1437 : .method = MHD_HTTP_METHOD_GET,
1438 : .permission = "categories-read",
1439 : .have_id_segment = true,
1440 : .allow_deleted_instance = true,
1441 : .handler = &TMH_private_get_categories_ID
1442 : },
1443 : /* DELETE /categories/$ID: */
1444 : {
1445 : .url_prefix = "/categories/",
1446 : .method = MHD_HTTP_METHOD_DELETE,
1447 : .permission = "categories-write",
1448 : .have_id_segment = true,
1449 : .allow_deleted_instance = true,
1450 : .handler = &TMH_private_delete_categories_ID
1451 : },
1452 : /* PATCH /categories/$ID/: */
1453 : {
1454 : .url_prefix = "/categories/",
1455 : .method = MHD_HTTP_METHOD_PATCH,
1456 : .permission = "categories-write",
1457 : .have_id_segment = true,
1458 : .allow_deleted_instance = true,
1459 : .handler = &TMH_private_patch_categories_ID,
1460 : /* allow category data of up to 8 kb, that should be plenty */
1461 : .max_upload = 1024 * 8
1462 : },
1463 : /* GET /units: */
1464 : {
1465 : .url_prefix = "/units",
1466 : .method = MHD_HTTP_METHOD_GET,
1467 : .handler = &TMH_private_get_units
1468 : },
1469 : /* POST /units: */
1470 : {
1471 : .url_prefix = "/units",
1472 : .method = MHD_HTTP_METHOD_POST,
1473 : .permission = "units-write",
1474 : .handler = &TMH_private_post_units,
1475 : .max_upload = 1024 * 8
1476 : },
1477 : /* GET /units/$UNIT: */
1478 : {
1479 : .url_prefix = "/units/",
1480 : .method = MHD_HTTP_METHOD_GET,
1481 : .have_id_segment = true,
1482 : .allow_deleted_instance = true,
1483 : .handler = &TMH_private_get_units_ID
1484 : },
1485 : /* DELETE /units/$UNIT: */
1486 : {
1487 : .url_prefix = "/units/",
1488 : .method = MHD_HTTP_METHOD_DELETE,
1489 : .permission = "units-write",
1490 : .have_id_segment = true,
1491 : .allow_deleted_instance = true,
1492 : .handler = &TMH_private_delete_units_ID
1493 : },
1494 : /* PATCH /units/$UNIT: */
1495 : {
1496 : .url_prefix = "/units/",
1497 : .method = MHD_HTTP_METHOD_PATCH,
1498 : .permission = "units-write",
1499 : .have_id_segment = true,
1500 : .allow_deleted_instance = true,
1501 : .handler = &TMH_private_patch_units_ID,
1502 : .max_upload = 1024 * 8
1503 : },
1504 : /* GET /products: */
1505 : {
1506 : .url_prefix = "/products",
1507 : .permission = "products-read",
1508 : .method = MHD_HTTP_METHOD_GET,
1509 : .handler = &TMH_private_get_products
1510 : },
1511 : /* POST /products: */
1512 : {
1513 : .url_prefix = "/products",
1514 : .method = MHD_HTTP_METHOD_POST,
1515 : .permission = "products-write",
1516 : .handler = &TMH_private_post_products,
1517 : /* allow product data of up to 8 MB, that should be plenty;
1518 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1519 : would require further changes to the allocation logic
1520 : in the code... */
1521 : .max_upload = 1024 * 1024 * 8
1522 : },
1523 : /* GET /products/$ID: */
1524 : {
1525 : .url_prefix = "/products/",
1526 : .method = MHD_HTTP_METHOD_GET,
1527 : .have_id_segment = true,
1528 : .permission = "products-read",
1529 : .allow_deleted_instance = true,
1530 : .handler = &TMH_private_get_products_ID
1531 : },
1532 : /* DELETE /products/$ID/: */
1533 : {
1534 : .url_prefix = "/products/",
1535 : .method = MHD_HTTP_METHOD_DELETE,
1536 : .have_id_segment = true,
1537 : .permission = "products-write",
1538 : .allow_deleted_instance = true,
1539 : .handler = &TMH_private_delete_products_ID
1540 : },
1541 : /* PATCH /products/$ID/: */
1542 : {
1543 : .url_prefix = "/products/",
1544 : .method = MHD_HTTP_METHOD_PATCH,
1545 : .have_id_segment = true,
1546 : .allow_deleted_instance = true,
1547 : .permission = "products-write",
1548 : .handler = &TMH_private_patch_products_ID,
1549 : /* allow product data of up to 8 MB, that should be plenty;
1550 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1551 : would require further changes to the allocation logic
1552 : in the code... */
1553 : .max_upload = 1024 * 1024 * 8
1554 : },
1555 : /* POST /products/$ID/lock: */
1556 : {
1557 : .url_prefix = "/products/",
1558 : .url_suffix = "lock",
1559 : .method = MHD_HTTP_METHOD_POST,
1560 : .have_id_segment = true,
1561 : .permission = "products-lock",
1562 : .handler = &TMH_private_post_products_ID_lock,
1563 : /* the body should be pretty small, allow 1 MB of upload
1564 : to set a conservative bound for sane wallets */
1565 : .max_upload = 1024 * 1024
1566 : },
1567 : /* POST /orders: */
1568 : {
1569 : .url_prefix = "/orders",
1570 : .method = MHD_HTTP_METHOD_POST,
1571 : .permission = "orders-write",
1572 : .handler = &TMH_private_post_orders,
1573 : /* allow contracts of up to 8 MB, that should be plenty;
1574 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1575 : would require further changes to the allocation logic
1576 : in the code... */
1577 : .max_upload = 1024 * 1024 * 8
1578 : },
1579 : /* GET /orders/$ID: */
1580 : {
1581 : .url_prefix = "/orders/",
1582 : .method = MHD_HTTP_METHOD_GET,
1583 : .permission = "orders-read",
1584 : .have_id_segment = true,
1585 : .allow_deleted_instance = true,
1586 : .handler = &TMH_private_get_orders_ID
1587 : },
1588 : /* GET /orders: */
1589 : {
1590 : .url_prefix = "/orders",
1591 : .method = MHD_HTTP_METHOD_GET,
1592 : .permission = "orders-read",
1593 : .allow_deleted_instance = true,
1594 : .handler = &TMH_private_get_orders
1595 : },
1596 : /* POST /orders/$ID/refund: */
1597 : {
1598 : .url_prefix = "/orders/",
1599 : .url_suffix = "refund",
1600 : .method = MHD_HTTP_METHOD_POST,
1601 : .have_id_segment = true,
1602 : .permission = "orders-refund",
1603 : .handler = &TMH_private_post_orders_ID_refund,
1604 : /* the body should be pretty small, allow 1 MB of upload
1605 : to set a conservative bound for sane wallets */
1606 : .max_upload = 1024 * 1024
1607 : },
1608 : /* PATCH /orders/$ID/forget: */
1609 : {
1610 : .url_prefix = "/orders/",
1611 : .url_suffix = "forget",
1612 : .method = MHD_HTTP_METHOD_PATCH,
1613 : .permission = "orders-write",
1614 : .have_id_segment = true,
1615 : .allow_deleted_instance = true,
1616 : .handler = &TMH_private_patch_orders_ID_forget,
1617 : /* the body should be pretty small, allow 1 MB of upload
1618 : to set a conservative bound for sane wallets */
1619 : .max_upload = 1024 * 1024
1620 : },
1621 : /* DELETE /orders/$ID: */
1622 : {
1623 : .url_prefix = "/orders/",
1624 : .method = MHD_HTTP_METHOD_DELETE,
1625 : .permission = "orders-write",
1626 : .have_id_segment = true,
1627 : .allow_deleted_instance = true,
1628 : .handler = &TMH_private_delete_orders_ID
1629 : },
1630 : /* POST /transfers: */
1631 : {
1632 : .url_prefix = "/transfers",
1633 : .method = MHD_HTTP_METHOD_POST,
1634 : .allow_deleted_instance = true,
1635 : .handler = &TMH_private_post_transfers,
1636 : .permission = "transfers-write",
1637 : /* the body should be pretty small, allow 1 MB of upload
1638 : to set a conservative bound for sane wallets */
1639 : .max_upload = 1024 * 1024
1640 : },
1641 : /* DELETE /transfers/$ID: */
1642 : {
1643 : .url_prefix = "/transfers/",
1644 : .method = MHD_HTTP_METHOD_DELETE,
1645 : .permission = "transfers-write",
1646 : .allow_deleted_instance = true,
1647 : .handler = &TMH_private_delete_transfers_ID,
1648 : .have_id_segment = true,
1649 : /* the body should be pretty small, allow 1 MB of upload
1650 : to set a conservative bound for sane wallets */
1651 : .max_upload = 1024 * 1024
1652 : },
1653 : /* GET /transfers: */
1654 : {
1655 : .url_prefix = "/transfers",
1656 : .permission = "transfers-read",
1657 : .method = MHD_HTTP_METHOD_GET,
1658 : .allow_deleted_instance = true,
1659 : .handler = &TMH_private_get_transfers
1660 : },
1661 : /* GET /incoming: */
1662 : {
1663 : .url_prefix = "/incoming",
1664 : .permission = "transfers-read",
1665 : .method = MHD_HTTP_METHOD_GET,
1666 : .allow_deleted_instance = true,
1667 : .handler = &TMH_private_get_incoming
1668 : },
1669 : /* POST /otp-devices: */
1670 : {
1671 : .url_prefix = "/otp-devices",
1672 : .permission = "otp-devices-write",
1673 : .method = MHD_HTTP_METHOD_POST,
1674 : .handler = &TMH_private_post_otp_devices
1675 : },
1676 : /* GET /otp-devices: */
1677 : {
1678 : .url_prefix = "/otp-devices",
1679 : .permission = "opt-devices-read",
1680 : .method = MHD_HTTP_METHOD_GET,
1681 : .handler = &TMH_private_get_otp_devices
1682 : },
1683 : /* GET /otp-devices/$ID/: */
1684 : {
1685 : .url_prefix = "/otp-devices/",
1686 : .method = MHD_HTTP_METHOD_GET,
1687 : .permission = "otp-devices-read",
1688 : .have_id_segment = true,
1689 : .handler = &TMH_private_get_otp_devices_ID
1690 : },
1691 : /* DELETE /otp-devices/$ID/: */
1692 : {
1693 : .url_prefix = "/otp-devices/",
1694 : .method = MHD_HTTP_METHOD_DELETE,
1695 : .permission = "otp-devices-write",
1696 : .have_id_segment = true,
1697 : .handler = &TMH_private_delete_otp_devices_ID
1698 : },
1699 : /* PATCH /otp-devices/$ID/: */
1700 : {
1701 : .url_prefix = "/otp-devices/",
1702 : .method = MHD_HTTP_METHOD_PATCH,
1703 : .permission = "otp-devices-write",
1704 : .have_id_segment = true,
1705 : .handler = &TMH_private_patch_otp_devices_ID
1706 : },
1707 : /* POST /templates: */
1708 : {
1709 : .url_prefix = "/templates",
1710 : .method = MHD_HTTP_METHOD_POST,
1711 : .permission = "templates-write",
1712 : .handler = &TMH_private_post_templates,
1713 : /* allow template data of up to 8 MB, that should be plenty;
1714 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1715 : would require further changes to the allocation logic
1716 : in the code... */
1717 : .max_upload = 1024 * 1024 * 8
1718 : },
1719 : /* GET /templates: */
1720 : {
1721 : .url_prefix = "/templates",
1722 : .permission = "templates-read",
1723 : .method = MHD_HTTP_METHOD_GET,
1724 : .handler = &TMH_private_get_templates
1725 : },
1726 : /* GET /templates/$ID/: */
1727 : {
1728 : .url_prefix = "/templates/",
1729 : .method = MHD_HTTP_METHOD_GET,
1730 : .permission = "templates-read",
1731 : .have_id_segment = true,
1732 : .allow_deleted_instance = true,
1733 : .handler = &TMH_private_get_templates_ID
1734 : },
1735 : /* DELETE /templates/$ID/: */
1736 : {
1737 : .url_prefix = "/templates/",
1738 : .method = MHD_HTTP_METHOD_DELETE,
1739 : .permission = "templates-write",
1740 : .have_id_segment = true,
1741 : .allow_deleted_instance = true,
1742 : .handler = &TMH_private_delete_templates_ID
1743 : },
1744 : /* PATCH /templates/$ID/: */
1745 : {
1746 : .url_prefix = "/templates/",
1747 : .method = MHD_HTTP_METHOD_PATCH,
1748 : .permission = "templates-write",
1749 : .have_id_segment = true,
1750 : .allow_deleted_instance = true,
1751 : .handler = &TMH_private_patch_templates_ID,
1752 : /* allow template data of up to 8 MB, that should be plenty;
1753 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1754 : would require further changes to the allocation logic
1755 : in the code... */
1756 : .max_upload = 1024 * 1024 * 8
1757 : },
1758 : /* GET /webhooks: */
1759 : {
1760 : .url_prefix = "/webhooks",
1761 : .permission = "webhooks-read",
1762 : .method = MHD_HTTP_METHOD_GET,
1763 : .handler = &TMH_private_get_webhooks
1764 : },
1765 : /* POST /webhooks: */
1766 : {
1767 : .url_prefix = "/webhooks",
1768 : .method = MHD_HTTP_METHOD_POST,
1769 : .permission = "webhooks-write",
1770 : .handler = &TMH_private_post_webhooks,
1771 : /* allow webhook data of up to 8 MB, that should be plenty;
1772 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1773 : would require further changes to the allocation logic
1774 : in the code... */
1775 : .max_upload = 1024 * 1024 * 8
1776 : },
1777 : /* GET /webhooks/$ID/: */
1778 : {
1779 : .url_prefix = "/webhooks/",
1780 : .method = MHD_HTTP_METHOD_GET,
1781 : .permission = "webhooks-read",
1782 : .have_id_segment = true,
1783 : .allow_deleted_instance = true,
1784 : .handler = &TMH_private_get_webhooks_ID
1785 : },
1786 : /* DELETE /webhooks/$ID/: */
1787 : {
1788 : .url_prefix = "/webhooks/",
1789 : .permission = "webhooks-write",
1790 : .method = MHD_HTTP_METHOD_DELETE,
1791 : .have_id_segment = true,
1792 : .allow_deleted_instance = true,
1793 : .handler = &TMH_private_delete_webhooks_ID
1794 : },
1795 : /* PATCH /webhooks/$ID/: */
1796 : {
1797 : .url_prefix = "/webhooks/",
1798 : .method = MHD_HTTP_METHOD_PATCH,
1799 : .permission = "webhooks-write",
1800 : .have_id_segment = true,
1801 : .allow_deleted_instance = true,
1802 : .handler = &TMH_private_patch_webhooks_ID,
1803 : /* allow webhook data of up to 8 MB, that should be plenty;
1804 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
1805 : would require further changes to the allocation logic
1806 : in the code... */
1807 : .max_upload = 1024 * 1024 * 8
1808 : },
1809 : /* POST /accounts: */
1810 : {
1811 : .url_prefix = "/accounts",
1812 : .method = MHD_HTTP_METHOD_POST,
1813 : .permission = "accounts-write",
1814 : .handler = &TMH_private_post_account,
1815 : /* allow account details of up to 8 kb, that should be plenty */
1816 : .max_upload = 1024 * 8
1817 : },
1818 : /* PATCH /accounts/$H_WIRE: */
1819 : {
1820 : .url_prefix = "/accounts/",
1821 : .method = MHD_HTTP_METHOD_PATCH,
1822 : .permission = "accounts-write",
1823 : .handler = &TMH_private_patch_accounts_ID,
1824 : .have_id_segment = true,
1825 : /* allow account details of up to 8 kb, that should be plenty */
1826 : .max_upload = 1024 * 8
1827 : },
1828 : /* GET /accounts: */
1829 : {
1830 : .url_prefix = "/accounts",
1831 : .permission = "accounts-read",
1832 : .method = MHD_HTTP_METHOD_GET,
1833 : .handler = &TMH_private_get_accounts
1834 : },
1835 : /* GET /accounts/$H_WIRE: */
1836 : {
1837 : .url_prefix = "/accounts/",
1838 : .permission = "accounts-read",
1839 : .method = MHD_HTTP_METHOD_GET,
1840 : .have_id_segment = true,
1841 : .handler = &TMH_private_get_accounts_ID
1842 : },
1843 : /* DELETE /accounts/$H_WIRE: */
1844 : {
1845 : .url_prefix = "/accounts/",
1846 : .permission = "accounts-write",
1847 : .method = MHD_HTTP_METHOD_DELETE,
1848 : .handler = &TMH_private_delete_account_ID,
1849 : .have_id_segment = true
1850 : },
1851 : /* GET /tokens: */
1852 : {
1853 : .url_prefix = "/tokens",
1854 : .permission = "tokens-read",
1855 : .method = MHD_HTTP_METHOD_GET,
1856 : .handler = &TMH_private_get_instances_ID_tokens,
1857 : },
1858 : /* POST /token: */
1859 : {
1860 : .url_prefix = "/token",
1861 : .permission = "token-refresh",
1862 : .method = MHD_HTTP_METHOD_POST,
1863 : .handler = &TMH_private_post_instances_ID_token,
1864 : /* Body should be tiny. */
1865 : .max_upload = 1024
1866 : },
1867 : /* DELETE /tokens/$SERIAL: */
1868 : {
1869 : .url_prefix = "/tokens/",
1870 : .permission = "tokens-write",
1871 : .method = MHD_HTTP_METHOD_DELETE,
1872 : .handler = &TMH_private_delete_instances_ID_token_SERIAL,
1873 : .have_id_segment = true
1874 : },
1875 : /* DELETE /token: */
1876 : {
1877 : .url_prefix = "/token",
1878 : .method = MHD_HTTP_METHOD_DELETE,
1879 : .handler = &TMH_private_delete_instances_ID_token,
1880 : },
1881 : /* GET /tokenfamilies: */
1882 : {
1883 : .url_prefix = "/tokenfamilies",
1884 : .permission = "tokenfamilies-read",
1885 : .method = MHD_HTTP_METHOD_GET,
1886 : .handler = &TMH_private_get_tokenfamilies
1887 : },
1888 : /* POST /tokenfamilies: */
1889 : {
1890 : .url_prefix = "/tokenfamilies",
1891 : .permission = "tokenfamilies-write",
1892 : .method = MHD_HTTP_METHOD_POST,
1893 : .handler = &TMH_private_post_token_families
1894 : },
1895 : /* GET /tokenfamilies/$SLUG/: */
1896 : {
1897 : .url_prefix = "/tokenfamilies/",
1898 : .method = MHD_HTTP_METHOD_GET,
1899 : .permission = "tokenfamilies-read",
1900 : .have_id_segment = true,
1901 : .handler = &TMH_private_get_tokenfamilies_SLUG
1902 : },
1903 : /* DELETE /tokenfamilies/$SLUG/: */
1904 : {
1905 : .url_prefix = "/tokenfamilies/",
1906 : .method = MHD_HTTP_METHOD_DELETE,
1907 : .permission = "tokenfamilies-write",
1908 : .have_id_segment = true,
1909 : .handler = &TMH_private_delete_token_families_SLUG
1910 : },
1911 : /* PATCH /tokenfamilies/$SLUG/: */
1912 : {
1913 : .url_prefix = "/tokenfamilies/",
1914 : .method = MHD_HTTP_METHOD_PATCH,
1915 : .permission = "tokenfamilies-write",
1916 : .have_id_segment = true,
1917 : .handler = &TMH_private_patch_token_family_SLUG,
1918 : },
1919 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
1920 : /* GET /donau */
1921 : {
1922 : .url_prefix = "/donau",
1923 : .method = MHD_HTTP_METHOD_GET,
1924 : .handler = &TMH_private_get_donau_instances
1925 : },
1926 : /* POST /donau */
1927 : {
1928 : .url_prefix = "/donau",
1929 : .method = MHD_HTTP_METHOD_POST,
1930 : .handler = &TMH_private_post_donau_instance
1931 : },
1932 : /* DELETE /donau/$charity-id */
1933 : {
1934 : .url_prefix = "/donau/",
1935 : .method = MHD_HTTP_METHOD_DELETE,
1936 : .have_id_segment = true,
1937 : .handler = &TMH_private_delete_donau_instance_ID
1938 : },
1939 : #endif
1940 : /* GET /statistics-counter/$SLUG: */
1941 : {
1942 : .url_prefix = "/statistics-counter/",
1943 : .method = MHD_HTTP_METHOD_GET,
1944 : .permission = "statistics-read",
1945 : .have_id_segment = true,
1946 : .handler = &TMH_private_get_statistics_counter_SLUG,
1947 : },
1948 : /* GET /statistics-amount/$SLUG: */
1949 : {
1950 : .url_prefix = "/statistics-amount/",
1951 : .method = MHD_HTTP_METHOD_GET,
1952 : .permission = "statistics-read",
1953 : .have_id_segment = true,
1954 : .handler = &TMH_private_get_statistics_amount_SLUG,
1955 : },
1956 : {
1957 : .url_prefix = NULL
1958 : }
1959 : };
1960 : static struct TMH_RequestHandler public_handlers[] = {
1961 : {
1962 : /* for "admin" instance, it does not even
1963 : have to exist before we give the WebUI */
1964 : .url_prefix = "/",
1965 : .method = MHD_HTTP_METHOD_GET,
1966 : .mime_type = "text/html",
1967 : .skip_instance = true,
1968 : .default_only = true,
1969 : .handler = &spa_redirect,
1970 : .response_code = MHD_HTTP_FOUND
1971 : },
1972 : {
1973 : .url_prefix = "/config",
1974 : .method = MHD_HTTP_METHOD_GET,
1975 : .skip_instance = true,
1976 : .default_only = true,
1977 : .handler = &MH_handler_config
1978 : },
1979 : {
1980 : /* for "normal" instance,s they must exist
1981 : before we give the WebUI */
1982 : .url_prefix = "/",
1983 : .method = MHD_HTTP_METHOD_GET,
1984 : .mime_type = "text/html",
1985 : .handler = &spa_redirect,
1986 : .response_code = MHD_HTTP_FOUND
1987 : },
1988 : {
1989 : .url_prefix = "/webui/",
1990 : .method = MHD_HTTP_METHOD_GET,
1991 : .mime_type = "text/html",
1992 : .skip_instance = true,
1993 : .have_id_segment = true,
1994 : .handler = &TMH_return_spa,
1995 : .response_code = MHD_HTTP_OK
1996 : },
1997 : {
1998 : .url_prefix = "/agpl",
1999 : .method = MHD_HTTP_METHOD_GET,
2000 : .skip_instance = true,
2001 : .handler = &TMH_MHD_handler_agpl_redirect
2002 : },
2003 : {
2004 : .url_prefix = "/agpl",
2005 : .method = MHD_HTTP_METHOD_GET,
2006 : .skip_instance = true,
2007 : .handler = &TMH_MHD_handler_agpl_redirect
2008 : },
2009 : {
2010 : .url_prefix = "/terms",
2011 : .method = MHD_HTTP_METHOD_GET,
2012 : .skip_instance = true,
2013 : .handler = &TMH_handler_terms
2014 : },
2015 : {
2016 : .url_prefix = "/privacy",
2017 : .method = MHD_HTTP_METHOD_GET,
2018 : .skip_instance = true,
2019 : .handler = &TMH_handler_privacy
2020 : },
2021 : /* Also serve the same /config per instance */
2022 : {
2023 : .url_prefix = "/config",
2024 : .method = MHD_HTTP_METHOD_GET,
2025 : .handler = &MH_handler_config
2026 : },
2027 : /* POST /orders/$ID/abort: */
2028 : {
2029 : .url_prefix = "/orders/",
2030 : .have_id_segment = true,
2031 : .url_suffix = "abort",
2032 : .method = MHD_HTTP_METHOD_POST,
2033 : .handler = &TMH_post_orders_ID_abort,
2034 : /* wallet may give us many coins to sign, allow 1 MB of upload
2035 : to set a conservative bound for sane wallets */
2036 : .max_upload = 1024 * 1024
2037 : },
2038 : /* POST /orders/$ID/claim: */
2039 : {
2040 : .url_prefix = "/orders/",
2041 : .have_id_segment = true,
2042 : .url_suffix = "claim",
2043 : .method = MHD_HTTP_METHOD_POST,
2044 : .handler = &TMH_post_orders_ID_claim,
2045 : /* the body should be pretty small, allow 1 MB of upload
2046 : to set a conservative bound for sane wallets */
2047 : .max_upload = 1024 * 1024
2048 : },
2049 : /* POST /orders/$ID/pay: */
2050 : {
2051 : .url_prefix = "/orders/",
2052 : .have_id_segment = true,
2053 : .url_suffix = "pay",
2054 : .method = MHD_HTTP_METHOD_POST,
2055 : .handler = &TMH_post_orders_ID_pay,
2056 : /* wallet may give us many coins to sign, allow 1 MB of upload
2057 : to set a conservative bound for sane wallets */
2058 : .max_upload = 1024 * 1024
2059 : },
2060 : /* POST /orders/$ID/paid: */
2061 : {
2062 : .url_prefix = "/orders/",
2063 : .have_id_segment = true,
2064 : .allow_deleted_instance = true,
2065 : .url_suffix = "paid",
2066 : .method = MHD_HTTP_METHOD_POST,
2067 : .handler = &TMH_post_orders_ID_paid,
2068 : /* the body should be pretty small, allow 1 MB of upload
2069 : to set a conservative bound for sane wallets */
2070 : .max_upload = 1024 * 1024
2071 : },
2072 : /* POST /orders/$ID/refund: */
2073 : {
2074 : .url_prefix = "/orders/",
2075 : .have_id_segment = true,
2076 : .allow_deleted_instance = true,
2077 : .url_suffix = "refund",
2078 : .method = MHD_HTTP_METHOD_POST,
2079 : .handler = &TMH_post_orders_ID_refund,
2080 : /* the body should be pretty small, allow 1 MB of upload
2081 : to set a conservative bound for sane wallets */
2082 : .max_upload = 1024 * 1024
2083 : },
2084 : /* GET /orders/$ID: */
2085 : {
2086 : .url_prefix = "/orders/",
2087 : .method = MHD_HTTP_METHOD_GET,
2088 : .allow_deleted_instance = true,
2089 : .have_id_segment = true,
2090 : .handler = &TMH_get_orders_ID
2091 : },
2092 : /* GET /static/ *: */
2093 : {
2094 : .url_prefix = "/static/",
2095 : .method = MHD_HTTP_METHOD_GET,
2096 : .have_id_segment = true,
2097 : .handler = &TMH_return_static
2098 : },
2099 : /* GET /templates/$ID/: */
2100 : {
2101 : .url_prefix = "/templates/",
2102 : .method = MHD_HTTP_METHOD_GET,
2103 : .have_id_segment = true,
2104 : .handler = &TMH_get_templates_ID
2105 : },
2106 : /* GET /products/$HASH/image: */
2107 : {
2108 : .url_prefix = "/products/",
2109 : .method = MHD_HTTP_METHOD_GET,
2110 : .have_id_segment = true,
2111 : .allow_deleted_instance = true,
2112 : .url_suffix = "image",
2113 : .handler = &TMH_get_products_image
2114 : },
2115 : /* POST /templates/$ID: */
2116 : {
2117 : .url_prefix = "/templates/",
2118 : .method = MHD_HTTP_METHOD_POST,
2119 : .have_id_segment = true,
2120 : .handler = &TMH_post_using_templates_ID,
2121 : .max_upload = 1024 * 1024
2122 : },
2123 : /* POST /challenge/$ID: */
2124 : {
2125 : .url_prefix = "/challenge/",
2126 : .method = MHD_HTTP_METHOD_POST,
2127 : .have_id_segment = true,
2128 : .handler = &TMH_post_challenge_ID,
2129 : .max_upload = 1024
2130 : },
2131 : /* POST /challenge/$ID/confirm: */
2132 : {
2133 : .url_prefix = "/challenge/",
2134 : .method = MHD_HTTP_METHOD_POST,
2135 : .have_id_segment = true,
2136 : .url_suffix = "confirm",
2137 : .handler = &TMH_post_challenge_ID_confirm,
2138 : .max_upload = 1024
2139 : },
2140 : /* POST /instances */
2141 : {
2142 : .url_prefix = "/instances",
2143 : .method = MHD_HTTP_METHOD_POST,
2144 : .skip_instance = true,
2145 : .default_only = true,
2146 : .handler = &TMH_public_post_instances,
2147 : /* allow instance data of up to 8 MB, that should be plenty;
2148 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
2149 : would require further changes to the allocation logic
2150 : in the code... */
2151 : .max_upload = 1024 * 1024 * 8
2152 : },
2153 : /* POST /forgot-password: */
2154 : {
2155 : .url_prefix = "/forgot-password",
2156 : .method = MHD_HTTP_METHOD_POST,
2157 : .handler = &TMH_public_post_instances_ID_auth,
2158 : /* Body should be pretty small. */
2159 : .max_upload = 1024 * 1024
2160 : },
2161 : {
2162 : .url_prefix = "*",
2163 : .method = MHD_HTTP_METHOD_OPTIONS,
2164 : .handler = &handle_server_options
2165 : },
2166 : {
2167 : .url_prefix = NULL
2168 : }
2169 : };
2170 2018 : struct TMH_HandlerContext *hc = *con_cls;
2171 : struct TMH_RequestHandler *handlers;
2172 2018 : bool use_default = false;
2173 :
2174 : (void) cls;
2175 : (void) version;
2176 2018 : if (NULL != hc->url)
2177 : {
2178 : /* MHD calls us again for a request, for first call
2179 : see 'else' case below */
2180 1269 : GNUNET_assert (NULL != hc->rh);
2181 1269 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
2182 1269 : if ( (hc->has_body) &&
2183 954 : (NULL == hc->request_body) )
2184 : {
2185 848 : size_t mul = hc->rh->max_upload;
2186 : enum GNUNET_GenericReturnValue res;
2187 :
2188 848 : if (0 == mul)
2189 20 : mul = DEFAULT_MAX_UPLOAD_SIZE;
2190 848 : if ( (hc->total_upload + *upload_data_size < hc->total_upload) ||
2191 848 : (hc->total_upload + *upload_data_size > mul) )
2192 : {
2193 : /* Client exceeds upload limit. Should _usually_ be checked earlier
2194 : when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with
2195 : chunked encoding an uploader MAY have omitted this, and thus
2196 : not permitted us to check on time. In this case, we just close
2197 : the connection once it exceeds our limit (instead of waiting
2198 : for the upload to complete and then fail). This could theoretically
2199 : cause some clients to retry, alas broken or malicious clients
2200 : are likely to retry anyway, so little we can do about it, and
2201 : failing earlier seems the best option here. */
2202 0 : GNUNET_break_op (0);
2203 0 : return MHD_NO;
2204 : }
2205 848 : hc->total_upload += *upload_data_size;
2206 848 : res = TALER_MHD_parse_post_json (connection,
2207 : &hc->json_parse_context,
2208 : upload_data,
2209 : upload_data_size,
2210 : &hc->request_body);
2211 848 : if (GNUNET_SYSERR == res)
2212 0 : return MHD_NO;
2213 : /* A error response was already generated */
2214 848 : if ( (GNUNET_NO == res) ||
2215 : /* or, need more data to accomplish parsing */
2216 848 : (NULL == hc->request_body) )
2217 424 : return MHD_YES; /* let MHD call us *again* */
2218 : }
2219 : /* Upload complete (if any), call handler to generate reply */
2220 845 : return hc->rh->handler (hc->rh,
2221 : connection,
2222 : hc);
2223 : }
2224 749 : hc->url = url;
2225 : {
2226 : const char *correlation_id;
2227 :
2228 749 : correlation_id = MHD_lookup_connection_value (connection,
2229 : MHD_HEADER_KIND,
2230 : "Taler-Correlation-Id");
2231 749 : if ( (NULL != correlation_id) &&
2232 : (GNUNET_YES !=
2233 0 : GNUNET_CURL_is_valid_scope_id (correlation_id)) )
2234 : {
2235 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2236 : "Illegal incoming correlation ID\n");
2237 0 : correlation_id = NULL;
2238 : }
2239 749 : if (NULL != correlation_id)
2240 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2241 : "Handling request for (%s) URL '%s', correlation_id=%s\n",
2242 : method,
2243 : url,
2244 : correlation_id);
2245 : else
2246 749 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2247 : "Handling request (%s) for URL '%s'\n",
2248 : method,
2249 : url);
2250 : }
2251 :
2252 749 : if (0 == strcasecmp (method,
2253 : MHD_HTTP_METHOD_HEAD))
2254 0 : method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
2255 :
2256 :
2257 : /* Find out the merchant backend instance for the request.
2258 : * If there is an instance, remove the instance specification
2259 : * from the beginning of the request URL. */
2260 : {
2261 749 : const char *instance_prefix = "/instances/";
2262 :
2263 749 : if (0 == strncmp (url,
2264 : instance_prefix,
2265 : strlen (instance_prefix)))
2266 : {
2267 : /* url starts with "/instances/" */
2268 83 : const char *istart = url + strlen (instance_prefix);
2269 83 : const char *slash = strchr (istart, '/');
2270 : char *instance_id;
2271 :
2272 83 : if (NULL == slash)
2273 0 : instance_id = GNUNET_strdup (istart);
2274 : else
2275 83 : instance_id = GNUNET_strndup (istart,
2276 : slash - istart);
2277 83 : if (0 == strcmp (instance_id,
2278 : "admin"))
2279 : {
2280 : MHD_RESULT ret;
2281 : struct MHD_Response *response;
2282 0 : const char *rstart = hc->full_url + strlen (instance_prefix);
2283 0 : const char *rslash = strchr (rstart, '/');
2284 :
2285 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2286 : "Client used deprecated '/instances/default/' path. Redirecting to modern path\n");
2287 :
2288 : response
2289 0 : = MHD_create_response_from_buffer (0,
2290 : NULL,
2291 : MHD_RESPMEM_PERSISTENT);
2292 0 : TALER_MHD_add_global_headers (response,
2293 : true);
2294 0 : if (MHD_NO ==
2295 0 : MHD_add_response_header (response,
2296 : MHD_HTTP_HEADER_LOCATION,
2297 : NULL == rslash
2298 : ? "/"
2299 : : rslash))
2300 : {
2301 0 : GNUNET_break (0);
2302 0 : MHD_destroy_response (response);
2303 0 : GNUNET_free (instance_id);
2304 0 : return MHD_NO;
2305 : }
2306 0 : ret = MHD_queue_response (connection,
2307 : MHD_HTTP_PERMANENT_REDIRECT,
2308 : response);
2309 0 : MHD_destroy_response (response);
2310 0 : GNUNET_free (instance_id);
2311 0 : return ret;
2312 : }
2313 83 : hc->instance = TMH_lookup_instance (instance_id);
2314 83 : if ( (NULL == hc->instance) &&
2315 2 : (0 == strcmp ("admin",
2316 : instance_id)) )
2317 0 : hc->instance = TMH_lookup_instance (NULL);
2318 83 : GNUNET_free (instance_id);
2319 83 : if (NULL == slash)
2320 0 : url = "";
2321 : else
2322 83 : url = slash;
2323 : }
2324 : else
2325 : {
2326 : /* use 'default' */
2327 666 : use_default = true;
2328 666 : hc->instance = TMH_lookup_instance (NULL);
2329 : }
2330 749 : if (NULL != hc->instance)
2331 : {
2332 705 : GNUNET_assert (hc->instance->rc < UINT_MAX);
2333 705 : hc->instance->rc++;
2334 : }
2335 : }
2336 :
2337 : {
2338 749 : const char *management_prefix = "/management/";
2339 749 : const char *private_prefix = "/private/";
2340 :
2341 749 : if ( (0 == strncmp (url,
2342 : management_prefix,
2343 : strlen (management_prefix))) )
2344 : {
2345 64 : handlers = management_handlers;
2346 64 : url += strlen (management_prefix) - 1;
2347 : }
2348 685 : else if ( (0 == strncmp (url,
2349 : private_prefix,
2350 212 : strlen (private_prefix))) ||
2351 212 : (0 == strcmp (url,
2352 : "/private")) )
2353 : {
2354 488 : handlers = private_handlers;
2355 976 : if (0 == strcmp (url,
2356 : "/private"))
2357 15 : url = "/";
2358 : else
2359 473 : url += strlen (private_prefix) - 1;
2360 : }
2361 : else
2362 : {
2363 197 : handlers = public_handlers;
2364 : }
2365 : }
2366 :
2367 749 : if (0 == strcmp (url,
2368 : ""))
2369 0 : url = "/"; /* code below does not like empty string */
2370 :
2371 : {
2372 : /* Matching URL found, but maybe method doesn't match */
2373 : size_t prefix_strlen; /* i.e. 8 for "/orders/", or 7 for "/config" */
2374 749 : const char *infix_url = NULL; /* i.e. "$ORDER_ID", no '/'-es */
2375 749 : size_t infix_strlen = 0; /* number of characters in infix_url */
2376 749 : const char *suffix_url = NULL; /* i.e. "refund", excludes '/' at the beginning */
2377 749 : size_t suffix_strlen = 0; /* number of characters in suffix_url */
2378 :
2379 : /* parse the URL into the three different components */
2380 : {
2381 : const char *slash;
2382 :
2383 749 : slash = strchr (&url[1], '/');
2384 749 : if (NULL == slash)
2385 : {
2386 : /* the prefix was everything */
2387 335 : prefix_strlen = strlen (url);
2388 : }
2389 : else
2390 : {
2391 414 : prefix_strlen = slash - url + 1; /* includes both '/'-es if present! */
2392 414 : infix_url = slash + 1;
2393 414 : slash = strchr (infix_url, '/');
2394 414 : if (NULL == slash)
2395 : {
2396 : /* the infix was the rest */
2397 243 : infix_strlen = strlen (infix_url);
2398 : }
2399 : else
2400 : {
2401 171 : infix_strlen = slash - infix_url; /* excludes both '/'-es */
2402 171 : suffix_url = slash + 1; /* skip the '/' */
2403 171 : suffix_strlen = strlen (suffix_url);
2404 : }
2405 414 : hc->infix = GNUNET_strndup (infix_url,
2406 : infix_strlen);
2407 : }
2408 : }
2409 :
2410 : /* find matching handler */
2411 : {
2412 749 : bool url_found = false;
2413 :
2414 15802 : for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
2415 : {
2416 15802 : struct TMH_RequestHandler *rh = &handlers[i];
2417 :
2418 15802 : if (rh->default_only && (! use_default))
2419 12 : continue;
2420 15790 : if (! prefix_match (rh,
2421 : url,
2422 : prefix_strlen,
2423 : infix_url,
2424 : infix_strlen,
2425 : suffix_url,
2426 : suffix_strlen))
2427 14798 : continue;
2428 992 : url_found = true;
2429 992 : if (0 == strcasecmp (method,
2430 : MHD_HTTP_METHOD_OPTIONS))
2431 : {
2432 1 : return TALER_MHD_reply_cors_preflight (connection);
2433 : }
2434 991 : if ( (rh->method != NULL) &&
2435 991 : (0 != strcasecmp (method,
2436 : rh->method)) )
2437 243 : continue;
2438 748 : hc->rh = rh;
2439 748 : break;
2440 : }
2441 : /* Handle HTTP 405: METHOD NOT ALLOWED case */
2442 748 : if ( (NULL == hc->rh) &&
2443 : (url_found) )
2444 : {
2445 : struct MHD_Response *reply;
2446 : MHD_RESULT ret;
2447 0 : char *allowed = NULL;
2448 :
2449 0 : GNUNET_break_op (0);
2450 : /* compute 'Allowed:' header (required by HTTP spec for 405 replies) */
2451 0 : for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
2452 : {
2453 0 : struct TMH_RequestHandler *rh = &handlers[i];
2454 :
2455 0 : if (rh->default_only && (! use_default))
2456 0 : continue;
2457 0 : if (! prefix_match (rh,
2458 : url,
2459 : prefix_strlen,
2460 : infix_url,
2461 : infix_strlen,
2462 : suffix_url,
2463 : suffix_strlen))
2464 0 : continue;
2465 0 : if (NULL == allowed)
2466 : {
2467 0 : allowed = GNUNET_strdup (rh->method);
2468 : }
2469 : else
2470 : {
2471 : char *tmp;
2472 :
2473 0 : GNUNET_asprintf (&tmp,
2474 : "%s, %s",
2475 : allowed,
2476 : rh->method);
2477 0 : GNUNET_free (allowed);
2478 0 : allowed = tmp;
2479 : }
2480 0 : if (0 == strcasecmp (rh->method,
2481 : MHD_HTTP_METHOD_GET))
2482 : {
2483 : char *tmp;
2484 :
2485 0 : GNUNET_asprintf (&tmp,
2486 : "%s, %s",
2487 : allowed,
2488 : MHD_HTTP_METHOD_HEAD);
2489 0 : GNUNET_free (allowed);
2490 0 : allowed = tmp;
2491 : }
2492 : }
2493 0 : reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
2494 : method);
2495 0 : GNUNET_break (MHD_YES ==
2496 : MHD_add_response_header (reply,
2497 : MHD_HTTP_HEADER_ALLOW,
2498 : allowed));
2499 0 : GNUNET_free (allowed);
2500 0 : ret = MHD_queue_response (connection,
2501 : MHD_HTTP_METHOD_NOT_ALLOWED,
2502 : reply);
2503 0 : MHD_destroy_response (reply);
2504 0 : return ret;
2505 : }
2506 748 : if (NULL == hc->rh)
2507 : {
2508 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2509 : "Endpoint `%s' not known\n",
2510 : hc->url);
2511 0 : return TALER_MHD_reply_with_error (connection,
2512 : MHD_HTTP_NOT_FOUND,
2513 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
2514 : hc->url);
2515 : }
2516 : }
2517 : }
2518 : /* At this point, we must have found a handler */
2519 748 : GNUNET_assert (NULL != hc->rh);
2520 :
2521 : /* If an instance should be there, check one exists */
2522 748 : if ( (NULL == hc->instance) &&
2523 43 : (! hc->rh->skip_instance) )
2524 : {
2525 3 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2526 : "Instance for `%s' not known\n",
2527 : hc->url);
2528 3 : return TALER_MHD_reply_with_error (connection,
2529 : MHD_HTTP_NOT_FOUND,
2530 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
2531 3 : hc->infix);
2532 : }
2533 :
2534 : /* Access control for non-public handlers */
2535 745 : if (public_handlers != handlers)
2536 : {
2537 : const char *auth;
2538 548 : bool is_basic_auth = false;
2539 548 : bool auth_malformed = false;
2540 :
2541 548 : auth = MHD_lookup_connection_value (connection,
2542 : MHD_HEADER_KIND,
2543 : MHD_HTTP_HEADER_AUTHORIZATION);
2544 :
2545 548 : if (NULL != auth)
2546 : {
2547 71 : extract_auth (&auth,
2548 : &is_basic_auth);
2549 71 : if (NULL == auth)
2550 0 : auth_malformed = true;
2551 71 : hc->auth_token = auth;
2552 : }
2553 :
2554 : /* If we have zero configured instances (not even ones that have been
2555 : purged) or explicitly disabled authentication, THEN we accept anything
2556 : (no access control), as we then also have no data to protect. */
2557 548 : if ((0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) ||
2558 527 : (GNUNET_YES == TMH_auth_disabled))
2559 : {
2560 21 : hc->auth_scope = TMH_AS_ALL;
2561 : }
2562 527 : else if (is_basic_auth)
2563 : {
2564 14 : process_basic_auth (hc, auth);
2565 : }
2566 : else /* Check bearer token */
2567 : {
2568 : enum TALER_ErrorCode ec;
2569 :
2570 513 : ec = process_bearer_auth (hc,
2571 : auth);
2572 513 : if (TALER_EC_NONE != ec)
2573 : {
2574 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2575 : "Bearer authentication failed: %d\n",
2576 : (int) ec);
2577 19 : return TALER_MHD_reply_with_ec (connection,
2578 : ec,
2579 : NULL);
2580 : }
2581 : }
2582 : /* We grant access if:
2583 : - Endpoint does not require permissions
2584 : - Authorization scope of bearer token contains permissions
2585 : required by endpoint.
2586 : */
2587 548 : if ( (NULL != hc->rh->permission) &&
2588 535 : (! permission_in_scope (hc->rh->permission,
2589 : hc->auth_scope)))
2590 : {
2591 19 : if (auth_malformed &&
2592 0 : (TMH_AS_NONE == hc->auth_scope) )
2593 : {
2594 0 : GNUNET_break_op (0);
2595 0 : return TALER_MHD_reply_with_error (
2596 : connection,
2597 : MHD_HTTP_UNAUTHORIZED,
2598 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
2599 : "'" RFC_8959_PREFIX
2600 : "' prefix or 'Bearer' missing in 'Authorization' header");
2601 : }
2602 19 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2603 : "Credentials provided are %d which are insufficient for access to `%s'\n",
2604 : (int) hc->auth_scope,
2605 : hc->rh->permission);
2606 19 : return TALER_MHD_reply_with_error (connection,
2607 : MHD_HTTP_UNAUTHORIZED,
2608 : TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
2609 : "Check credentials in 'Authorization' header");
2610 : }
2611 : } /* if (use_private) */
2612 :
2613 :
2614 726 : if ( (NULL == hc->instance) &&
2615 38 : (! hc->rh->skip_instance) )
2616 : {
2617 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2618 : "Instance for URL `%s' not known\n",
2619 : url);
2620 0 : return TALER_MHD_reply_with_error (connection,
2621 : MHD_HTTP_NOT_FOUND,
2622 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
2623 : url);
2624 : }
2625 726 : if ( (NULL != hc->instance) && /* make static analysis happy */
2626 688 : (! hc->rh->skip_instance) &&
2627 646 : (hc->instance->deleted) &&
2628 3 : (! hc->rh->allow_deleted_instance) )
2629 : {
2630 3 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2631 : "Instance `%s' was deleted\n",
2632 : hc->instance->settings.id);
2633 3 : return TALER_MHD_reply_with_error (connection,
2634 : MHD_HTTP_NOT_FOUND,
2635 : TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED,
2636 3 : hc->instance->settings.id);
2637 : }
2638 : /* parse request body */
2639 1446 : hc->has_body = ( (0 == strcasecmp (method,
2640 1064 : MHD_HTTP_METHOD_POST)) ||
2641 : /* PUT is not yet used */
2642 341 : (0 == strcasecmp (method,
2643 723 : MHD_HTTP_METHOD_PATCH)) );
2644 723 : if (hc->has_body)
2645 : {
2646 424 : TALER_MHD_check_content_length (connection,
2647 : 0 == hc->rh->max_upload
2648 : ? DEFAULT_MAX_UPLOAD_SIZE
2649 : : hc->rh->max_upload);
2650 424 : GNUNET_break (NULL == hc->request_body); /* can't have it already */
2651 : }
2652 723 : return MHD_YES; /* wait for MHD to call us again */
2653 : }
2654 :
2655 :
2656 : /**
2657 : * Callback invoked with information about a bank account.
2658 : *
2659 : * @param cls closure with a `struct TMH_MerchantInstance *`
2660 : * @param merchant_priv private key of the merchant instance
2661 : * @param acc details about the account
2662 : */
2663 : static void
2664 33 : add_account_cb (void *cls,
2665 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
2666 : const struct TALER_MERCHANTDB_AccountDetails *acc)
2667 : {
2668 33 : struct TMH_MerchantInstance *mi = cls;
2669 : struct TMH_WireMethod *wm;
2670 :
2671 : (void) merchant_priv;
2672 33 : wm = GNUNET_new (struct TMH_WireMethod);
2673 33 : wm->h_wire = acc->h_wire;
2674 : wm->payto_uri.full_payto
2675 33 : = GNUNET_strdup (acc->payto_uri.full_payto);
2676 33 : wm->wire_salt = acc->salt;
2677 : wm->wire_method
2678 33 : = TALER_payto_get_method (acc->payto_uri.full_payto);
2679 33 : wm->active = acc->active;
2680 33 : GNUNET_CONTAINER_DLL_insert (mi->wm_head,
2681 : mi->wm_tail,
2682 : wm);
2683 33 : }
2684 :
2685 :
2686 : /**
2687 : * Function called during startup to add all known instances to our
2688 : * hash map in memory for faster lookups when we receive requests.
2689 : *
2690 : * @param cls closure, NULL, unused
2691 : * @param merchant_pub public key of the instance
2692 : * @param merchant_priv private key of the instance, NULL if not available
2693 : * @param is detailed configuration settings for the instance
2694 : * @param ias authentication settings for the instance
2695 : */
2696 : static void
2697 71 : add_instance_cb (void *cls,
2698 : const struct TALER_MerchantPublicKeyP *merchant_pub,
2699 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
2700 : const struct TALER_MERCHANTDB_InstanceSettings *is,
2701 : const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
2702 : {
2703 : struct TMH_MerchantInstance *mi;
2704 : enum GNUNET_DB_QueryStatus qs;
2705 :
2706 : (void) cls;
2707 71 : mi = TMH_lookup_instance (is->id);
2708 71 : if (NULL != mi)
2709 : {
2710 : /* (outdated) entry exists, remove old entry */
2711 0 : (void) TMH_instance_free_cb (NULL,
2712 0 : &mi->h_instance,
2713 : mi);
2714 : }
2715 71 : mi = GNUNET_new (struct TMH_MerchantInstance);
2716 71 : mi->settings = *is;
2717 71 : mi->auth = *ias;
2718 71 : mi->settings.id = GNUNET_strdup (mi->settings.id);
2719 71 : mi->settings.name = GNUNET_strdup (mi->settings.name);
2720 71 : if (NULL != mi->settings.email)
2721 0 : mi->settings.email = GNUNET_strdup (mi->settings.email);
2722 71 : if (NULL != mi->settings.phone)
2723 0 : mi->settings.phone = GNUNET_strdup (mi->settings.phone);
2724 71 : if (NULL != mi->settings.website)
2725 0 : mi->settings.website = GNUNET_strdup (mi->settings.website);
2726 71 : if (NULL != mi->settings.logo)
2727 0 : mi->settings.logo = GNUNET_strdup (mi->settings.logo);
2728 71 : mi->settings.address = json_incref (mi->settings.address);
2729 71 : mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
2730 71 : if (NULL != merchant_priv)
2731 67 : mi->merchant_priv = *merchant_priv;
2732 : else
2733 4 : mi->deleted = true;
2734 71 : mi->merchant_pub = *merchant_pub;
2735 71 : qs = TMH_db->select_accounts (TMH_db->cls,
2736 71 : mi->settings.id,
2737 : &add_account_cb,
2738 : mi);
2739 71 : if (0 > qs)
2740 : {
2741 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2742 : "Error loading accounts of `%s' from database\n",
2743 : mi->settings.id);
2744 : }
2745 71 : GNUNET_assert (GNUNET_OK ==
2746 : TMH_add_instance (mi));
2747 71 : }
2748 :
2749 :
2750 : /**
2751 : * Trigger (re)loading of instance settings from DB.
2752 : *
2753 : * @param cls NULL
2754 : * @param extra ID of the instance that changed, NULL
2755 : * to load all instances (will not handle purges!)
2756 : * @param extra_len number of bytes in @a extra
2757 : */
2758 : static void
2759 98 : load_instances (void *cls,
2760 : const void *extra,
2761 : size_t extra_len)
2762 : {
2763 : enum GNUNET_DB_QueryStatus qs;
2764 98 : const char *id = extra;
2765 :
2766 : (void) cls;
2767 98 : if ( (NULL != extra) &&
2768 83 : ( (0 == extra_len) ||
2769 83 : ('\0' != id[extra_len - 1]) ) )
2770 : {
2771 0 : GNUNET_break (0 == extra_len);
2772 0 : extra = NULL;
2773 : }
2774 98 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2775 : "Received instance settings notification: reload `%s'\n",
2776 : id);
2777 98 : if (NULL == extra)
2778 : {
2779 15 : qs = TMH_db->lookup_instances (TMH_db->cls,
2780 : false,
2781 : &add_instance_cb,
2782 : NULL);
2783 : }
2784 : else
2785 : {
2786 : struct TMH_MerchantInstance *mi;
2787 :
2788 : /* This must be done here to handle instance
2789 : purging, as for purged instances, the DB
2790 : lookup below will otherwise do nothing */
2791 83 : mi = TMH_lookup_instance (id);
2792 83 : if (NULL != mi)
2793 : {
2794 83 : (void) TMH_instance_free_cb (NULL,
2795 83 : &mi->h_instance,
2796 : mi);
2797 : }
2798 83 : qs = TMH_db->lookup_instance (TMH_db->cls,
2799 : id,
2800 : false,
2801 : &add_instance_cb,
2802 : NULL);
2803 : }
2804 98 : if (0 > qs)
2805 : {
2806 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2807 : "Failed initialization. Check database setup.\n");
2808 0 : global_ret = EXIT_NOPERMISSION;
2809 0 : GNUNET_SCHEDULER_shutdown ();
2810 0 : return;
2811 : }
2812 : }
2813 :
2814 :
2815 : /**
2816 : * A transaction modified an instance setting (or created/deleted/purged
2817 : * one). Notify all backends about the change.
2818 : *
2819 : * @param id ID of the instance that changed
2820 : */
2821 : void
2822 83 : TMH_reload_instances (const char *id)
2823 : {
2824 83 : struct GNUNET_DB_EventHeaderP es = {
2825 83 : .size = ntohs (sizeof (es)),
2826 83 : .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
2827 : };
2828 :
2829 83 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2830 : "Generating instance settings notification: reload `%s'\n",
2831 : id);
2832 166 : TMH_db->event_notify (TMH_db->cls,
2833 : &es,
2834 : id,
2835 : (NULL == id)
2836 : ? 0
2837 83 : : strlen (id) + 1);
2838 83 : }
2839 :
2840 :
2841 : /**
2842 : * Callback invoked on every listen socket to start the
2843 : * respective MHD HTTP daemon.
2844 : *
2845 : * @param cls unused
2846 : * @param lsock the listen socket
2847 : */
2848 : static void
2849 30 : start_daemon (void *cls,
2850 : int lsock)
2851 : {
2852 : struct MHD_Daemon *mhd;
2853 :
2854 : (void) cls;
2855 30 : GNUNET_assert (-1 != lsock);
2856 30 : mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK
2857 : | MHD_USE_AUTO,
2858 : 0 /* port */,
2859 : NULL, NULL,
2860 : &url_handler, NULL,
2861 : MHD_OPTION_LISTEN_SOCKET, lsock,
2862 : MHD_OPTION_URI_LOG_CALLBACK,
2863 : &full_url_track_callback, NULL,
2864 : MHD_OPTION_NOTIFY_COMPLETED,
2865 : &handle_mhd_completion_callback, NULL,
2866 : MHD_OPTION_CONNECTION_TIMEOUT,
2867 : (unsigned int) 10 /* 10s */,
2868 : MHD_OPTION_END);
2869 30 : if (NULL == mhd)
2870 : {
2871 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2872 : "Failed to launch HTTP service.\n");
2873 0 : GNUNET_SCHEDULER_shutdown ();
2874 0 : return;
2875 : }
2876 30 : have_daemons = true;
2877 30 : TALER_MHD_daemon_start (mhd);
2878 : }
2879 :
2880 :
2881 : /**
2882 : * Main function that will be run by the scheduler.
2883 : *
2884 : * @param cls closure
2885 : * @param args remaining command-line arguments
2886 : * @param cfgfile name of the configuration file used (for saving, can be
2887 : * NULL!)
2888 : * @param config configuration
2889 : */
2890 : static void
2891 15 : run (void *cls,
2892 : char *const *args,
2893 : const char *cfgfile,
2894 : const struct GNUNET_CONFIGURATION_Handle *config)
2895 : {
2896 : enum TALER_MHD_GlobalOptions go;
2897 : int elen;
2898 :
2899 : (void) cls;
2900 : (void) args;
2901 : (void) cfgfile;
2902 15 : cfg = config;
2903 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2904 : "Starting taler-merchant-httpd\n");
2905 15 : go = TALER_MHD_GO_NONE;
2906 15 : if (merchant_connection_close)
2907 0 : go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
2908 15 : TALER_MHD_setup (go);
2909 :
2910 15 : global_ret = EXIT_SUCCESS;
2911 15 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2912 : NULL);
2913 :
2914 : TMH_curl_ctx
2915 15 : = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
2916 : &merchant_curl_rc);
2917 15 : if (NULL == TMH_curl_ctx)
2918 : {
2919 0 : GNUNET_break (0);
2920 0 : global_ret = EXIT_NO_RESTART;
2921 0 : GNUNET_SCHEDULER_shutdown ();
2922 0 : return;
2923 : }
2924 15 : merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx);
2925 : /* Disable 100 continue processing */
2926 15 : GNUNET_break (GNUNET_OK ==
2927 : GNUNET_CURL_append_header (TMH_curl_ctx,
2928 : MHD_HTTP_HEADER_EXPECT ":"));
2929 15 : GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx,
2930 : "Taler-Correlation-Id");
2931 :
2932 15 : if (GNUNET_SYSERR ==
2933 15 : TALER_config_get_currency (cfg,
2934 : "merchant",
2935 : &TMH_currency))
2936 : {
2937 0 : global_ret = EXIT_NOTCONFIGURED;
2938 0 : GNUNET_SCHEDULER_shutdown ();
2939 0 : return;
2940 : }
2941 15 : if (GNUNET_OK !=
2942 15 : TALER_CONFIG_parse_currencies (cfg,
2943 : TMH_currency,
2944 : &TMH_num_cspecs,
2945 : &TMH_cspecs))
2946 : {
2947 0 : global_ret = EXIT_NOTCONFIGURED;
2948 0 : GNUNET_SCHEDULER_shutdown ();
2949 0 : return;
2950 : }
2951 :
2952 15 : if (GNUNET_SYSERR ==
2953 : (TMH_strict_v19
2954 15 : = GNUNET_CONFIGURATION_get_value_yesno (cfg,
2955 : "merchant",
2956 : "STRICT_PROTOCOL_V19")))
2957 : {
2958 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
2959 : "merchant",
2960 : "STRICT_PROTOCOL_V19");
2961 0 : TMH_strict_v19 = GNUNET_NO;
2962 : }
2963 15 : if (GNUNET_SYSERR ==
2964 15 : (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (cfg,
2965 : "merchant",
2966 : "DISABLE_AUTHENTICATION")))
2967 : {
2968 0 : TMH_auth_disabled = GNUNET_NO;
2969 : }
2970 15 : if (GNUNET_YES == TMH_auth_disabled)
2971 : {
2972 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2973 : "DANGEROUS: Endpoint Authentication disabled!");
2974 : }
2975 :
2976 15 : if (GNUNET_SYSERR ==
2977 : (TMH_have_self_provisioning
2978 15 : = GNUNET_CONFIGURATION_get_value_yesno (cfg,
2979 : "merchant",
2980 : "ENABLE_SELF_PROVISIONING")))
2981 : {
2982 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
2983 : "merchant",
2984 : "ENABLE_SELF_PROVISIONING");
2985 0 : TMH_have_self_provisioning = GNUNET_NO;
2986 : }
2987 :
2988 15 : if (GNUNET_OK !=
2989 15 : GNUNET_CONFIGURATION_get_value_time (cfg,
2990 : "merchant",
2991 : "LEGAL_PRESERVATION",
2992 : &TMH_legal_expiration))
2993 : {
2994 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2995 : "merchant",
2996 : "LEGAL_PRESERVATION");
2997 0 : global_ret = EXIT_NOTCONFIGURED;
2998 0 : GNUNET_SCHEDULER_shutdown ();
2999 0 : return;
3000 : }
3001 :
3002 15 : if (GNUNET_OK !=
3003 15 : GNUNET_CONFIGURATION_get_value_time (cfg,
3004 : "merchant",
3005 : "LEGAL_PRESERVATION",
3006 : &TMH_legal_expiration))
3007 : {
3008 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3009 : "merchant",
3010 : "LEGAL_PRESERVATION");
3011 0 : global_ret = EXIT_NOTCONFIGURED;
3012 0 : GNUNET_SCHEDULER_shutdown ();
3013 0 : return;
3014 : }
3015 :
3016 15 : if (GNUNET_OK !=
3017 15 : GNUNET_CONFIGURATION_get_value_time (cfg,
3018 : "merchant",
3019 : "DEFAULT_PAY_DELAY",
3020 : &TMH_default_pay_delay))
3021 : {
3022 15 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
3023 : "merchant",
3024 : "DEFAULT_PAY_DELAY");
3025 15 : TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS;
3026 : }
3027 15 : if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay))
3028 : {
3029 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
3030 : "merchant",
3031 : "DEFAULT_PAY_DELAY",
3032 : "forever is not allowed");
3033 0 : global_ret = EXIT_NOTCONFIGURED;
3034 0 : GNUNET_SCHEDULER_shutdown ();
3035 0 : return;
3036 : }
3037 15 : if (GNUNET_OK !=
3038 15 : GNUNET_CONFIGURATION_get_value_time (cfg,
3039 : "merchant",
3040 : "DEFAULT_REFUND_DELAY",
3041 : &TMH_default_refund_delay))
3042 : {
3043 15 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
3044 : "merchant",
3045 : "DEFAULT_REFUND_DELAY");
3046 15 : TMH_default_refund_delay = GNUNET_TIME_relative_multiply (
3047 : GNUNET_TIME_UNIT_DAYS,
3048 : 15);
3049 : }
3050 15 : if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay))
3051 : {
3052 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
3053 : "merchant",
3054 : "DEFAULT_REFUND_DELAY",
3055 : "forever is not allowed");
3056 0 : global_ret = EXIT_NOTCONFIGURED;
3057 0 : GNUNET_SCHEDULER_shutdown ();
3058 0 : return;
3059 : }
3060 :
3061 15 : if (GNUNET_OK !=
3062 15 : GNUNET_CONFIGURATION_get_value_time (cfg,
3063 : "merchant",
3064 : "DEFAULT_WIRE_TRANSFER_DELAY",
3065 : &TMH_default_wire_transfer_delay))
3066 : {
3067 15 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
3068 : "merchant",
3069 : "DEFAULT_WIRE_TRANSFER_DELAY");
3070 15 : TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS;
3071 : }
3072 15 : if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay))
3073 : {
3074 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
3075 : "merchant",
3076 : "DEFAULT_WIRE_TRANSFER_DELAY",
3077 : "forever is not allowed");
3078 0 : global_ret = EXIT_NOTCONFIGURED;
3079 0 : GNUNET_SCHEDULER_shutdown ();
3080 0 : return;
3081 : }
3082 :
3083 : {
3084 : char *dwtri;
3085 :
3086 15 : if (GNUNET_OK !=
3087 15 : GNUNET_CONFIGURATION_get_value_string (
3088 : cfg,
3089 : "merchant",
3090 : "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
3091 : &dwtri))
3092 : {
3093 15 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
3094 : "merchant",
3095 : "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL");
3096 15 : TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE;
3097 : }
3098 : else
3099 : {
3100 0 : if (GNUNET_OK !=
3101 0 : GNUNET_TIME_string_to_round_interval (
3102 : dwtri,
3103 : &TMH_default_wire_transfer_rounding_interval))
3104 : {
3105 0 : GNUNET_log_config_invalid (
3106 : GNUNET_ERROR_TYPE_ERROR,
3107 : "merchant",
3108 : "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
3109 : "invalid time rounding interval");
3110 0 : global_ret = EXIT_NOTCONFIGURED;
3111 0 : GNUNET_free (dwtri);
3112 0 : GNUNET_SCHEDULER_shutdown ();
3113 0 : return;
3114 : }
3115 0 : GNUNET_free (dwtri);
3116 : }
3117 : }
3118 :
3119 15 : TMH_load_terms (cfg);
3120 :
3121 15 : if (GNUNET_OK !=
3122 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
3123 : "merchant",
3124 : "PAYMENT_TARGET_TYPES",
3125 : &TMH_allowed_payment_targets))
3126 : {
3127 15 : TMH_allowed_payment_targets = GNUNET_strdup ("*");
3128 : }
3129 :
3130 15 : if (GNUNET_OK !=
3131 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
3132 : "merchant",
3133 : "DEFAULT_PERSONA",
3134 : &TMH_default_persona))
3135 : {
3136 15 : TMH_default_persona = GNUNET_strdup ("expert");
3137 : }
3138 :
3139 15 : if (GNUNET_OK !=
3140 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
3141 : "merchant",
3142 : "PAYMENT_TARGET_REGEX",
3143 : &TMH_payment_target_regex))
3144 : {
3145 15 : TMH_payment_target_regex = NULL;
3146 : }
3147 : else
3148 : {
3149 0 : if (0 == strlen (TMH_payment_target_regex))
3150 : {
3151 0 : GNUNET_free (TMH_payment_target_regex);
3152 : }
3153 : else
3154 : {
3155 0 : if (0 != regcomp (&TMH_payment_target_re,
3156 : TMH_payment_target_regex,
3157 : REG_NOSUB | REG_EXTENDED))
3158 : {
3159 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
3160 : "merchant",
3161 : "PAYMENT_TARGET_REGEX",
3162 : "malformed regular expression");
3163 0 : global_ret = EXIT_NOTCONFIGURED;
3164 0 : GNUNET_free (TMH_payment_target_regex);
3165 0 : GNUNET_SCHEDULER_shutdown ();
3166 0 : return;
3167 : }
3168 : }
3169 : }
3170 :
3171 15 : if (GNUNET_OK !=
3172 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
3173 : "merchant",
3174 : "HELPER_SMS",
3175 : &TMH_helper_sms))
3176 : {
3177 15 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
3178 : "merchant",
3179 : "HELPER_SMS",
3180 : "no helper specified");
3181 : }
3182 :
3183 15 : if (GNUNET_OK !=
3184 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
3185 : "merchant",
3186 : "HELPER_EMAIL",
3187 : &TMH_helper_email))
3188 : {
3189 15 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
3190 : "merchant",
3191 : "HELPER_EMAIL",
3192 : "no helper specified");
3193 : }
3194 :
3195 : {
3196 : char *tan_channels;
3197 :
3198 15 : if (GNUNET_OK ==
3199 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
3200 : "merchant",
3201 : "MANDATORY_TAN_CHANNELS",
3202 : &tan_channels))
3203 : {
3204 0 : for (char *tok = strtok (tan_channels,
3205 : " ");
3206 0 : NULL != tok;
3207 0 : tok = strtok (NULL,
3208 : " "))
3209 : {
3210 0 : if (0 == strcasecmp (tok,
3211 : "sms"))
3212 : {
3213 0 : if (NULL == TMH_helper_sms)
3214 : {
3215 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
3216 : "merchant",
3217 : "MANDATORY_TAN_CHANNELS",
3218 : "SMS mandatory, but no HELPER_SMS configured");
3219 0 : global_ret = EXIT_NOTCONFIGURED;
3220 0 : GNUNET_SCHEDULER_shutdown ();
3221 0 : GNUNET_free (tan_channels);
3222 0 : return;
3223 : }
3224 0 : TEH_mandatory_tan_channels |= TEH_TCS_SMS;
3225 : }
3226 0 : else if (0 == strcasecmp (tok,
3227 : "email"))
3228 : {
3229 0 : if (NULL == TMH_helper_email)
3230 : {
3231 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
3232 : "merchant",
3233 : "MANDATORY_TAN_CHANNELS",
3234 : "EMAIL mandatory, but no HELPER_EMAIL configured");
3235 0 : global_ret = EXIT_NOTCONFIGURED;
3236 0 : GNUNET_SCHEDULER_shutdown ();
3237 0 : GNUNET_free (tan_channels);
3238 0 : return;
3239 : }
3240 0 : TEH_mandatory_tan_channels |= TEH_TCS_EMAIL;
3241 : }
3242 : else
3243 : {
3244 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
3245 : "merchant",
3246 : "MANDATORY_TAN_CHANNELS",
3247 : tok);
3248 0 : global_ret = EXIT_NOTCONFIGURED;
3249 0 : GNUNET_SCHEDULER_shutdown ();
3250 0 : GNUNET_free (tan_channels);
3251 0 : return;
3252 : }
3253 : }
3254 0 : GNUNET_free (tan_channels);
3255 : }
3256 : }
3257 :
3258 15 : if (GNUNET_OK ==
3259 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
3260 : "merchant",
3261 : "BASE_URL",
3262 : &TMH_base_url))
3263 : {
3264 0 : if (! TALER_is_web_url (TMH_base_url))
3265 : {
3266 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
3267 : "merchant",
3268 : "BASE_URL",
3269 : "Needs to start with 'http://' or 'https://'");
3270 0 : global_ret = EXIT_NOTCONFIGURED;
3271 0 : GNUNET_SCHEDULER_shutdown ();
3272 0 : return;
3273 : }
3274 : }
3275 15 : if (GNUNET_OK ==
3276 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
3277 : "merchant",
3278 : "BACKOFFICE_SPA_DIR",
3279 : &TMH_spa_dir))
3280 : {
3281 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3282 : "Loading merchant SPA from %s\n",
3283 : TMH_spa_dir);
3284 : }
3285 : else
3286 : {
3287 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3288 : "Loading merchant SPA from default location\n");
3289 : }
3290 :
3291 15 : if (GNUNET_YES ==
3292 15 : GNUNET_CONFIGURATION_get_value_yesno (cfg,
3293 : "merchant",
3294 : "FORCE_AUDIT"))
3295 11 : TMH_force_audit = GNUNET_YES;
3296 15 : if (GNUNET_OK !=
3297 15 : TALER_TEMPLATING_init (TALER_MERCHANT_project_data ()))
3298 : {
3299 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3300 : "Failed to setup templates\n");
3301 0 : global_ret = EXIT_NOTINSTALLED;
3302 0 : GNUNET_SCHEDULER_shutdown ();
3303 0 : return;
3304 : }
3305 15 : if (GNUNET_OK !=
3306 15 : TMH_spa_init (TMH_spa_dir))
3307 : {
3308 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3309 : "Failed to load single page app\n");
3310 0 : global_ret = EXIT_NOTINSTALLED;
3311 0 : GNUNET_SCHEDULER_shutdown ();
3312 0 : return;
3313 : }
3314 : /* /static/ is currently not used */
3315 : /* (void) TMH_statics_init (); */
3316 15 : if (NULL ==
3317 15 : (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4,
3318 : GNUNET_YES)))
3319 : {
3320 0 : global_ret = EXIT_FAILURE;
3321 0 : GNUNET_SCHEDULER_shutdown ();
3322 0 : return;
3323 : }
3324 15 : if (NULL ==
3325 15 : (TMH_db = TALER_MERCHANTDB_plugin_load (cfg)))
3326 : {
3327 0 : global_ret = EXIT_NOTINSTALLED;
3328 0 : GNUNET_SCHEDULER_shutdown ();
3329 0 : return;
3330 : }
3331 15 : if (GNUNET_OK !=
3332 15 : TMH_db->connect (TMH_db->cls))
3333 : {
3334 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3335 : "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
3336 0 : global_ret = EXIT_FAILURE;
3337 0 : GNUNET_SCHEDULER_shutdown ();
3338 0 : return;
3339 : }
3340 15 : elen = TMH_EXCHANGES_init (config);
3341 15 : if (GNUNET_SYSERR == elen)
3342 : {
3343 0 : global_ret = EXIT_NOTCONFIGURED;
3344 0 : GNUNET_SCHEDULER_shutdown ();
3345 0 : return;
3346 : }
3347 15 : if (0 == elen)
3348 : {
3349 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3350 : "Fatal: no trusted exchanges configured. Exiting.\n");
3351 0 : global_ret = EXIT_NOTCONFIGURED;
3352 0 : GNUNET_SCHEDULER_shutdown ();
3353 0 : return;
3354 : }
3355 :
3356 : {
3357 15 : struct GNUNET_DB_EventHeaderP es = {
3358 15 : .size = ntohs (sizeof (es)),
3359 15 : .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
3360 : };
3361 :
3362 30 : instance_eh = TMH_db->event_listen (TMH_db->cls,
3363 : &es,
3364 15 : GNUNET_TIME_UNIT_FOREVER_REL,
3365 : &load_instances,
3366 : NULL);
3367 : }
3368 15 : load_instances (NULL,
3369 : NULL,
3370 : 0);
3371 : {
3372 : enum GNUNET_GenericReturnValue ret;
3373 :
3374 15 : ret = TALER_MHD_listen_bind (cfg,
3375 : "merchant",
3376 : &start_daemon,
3377 : NULL);
3378 15 : switch (ret)
3379 : {
3380 0 : case GNUNET_SYSERR:
3381 0 : global_ret = EXIT_NOTCONFIGURED;
3382 0 : GNUNET_SCHEDULER_shutdown ();
3383 0 : return;
3384 0 : case GNUNET_NO:
3385 0 : if (! have_daemons)
3386 : {
3387 0 : global_ret = EXIT_NOTCONFIGURED;
3388 0 : GNUNET_SCHEDULER_shutdown ();
3389 0 : return;
3390 : }
3391 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3392 : "Could not open all configured listen sockets\n");
3393 0 : break;
3394 15 : case GNUNET_OK:
3395 15 : break;
3396 : }
3397 : }
3398 15 : global_ret = EXIT_SUCCESS;
3399 : }
3400 :
3401 :
3402 : /**
3403 : * The main function of the serve tool
3404 : *
3405 : * @param argc number of arguments from the command line
3406 : * @param argv command line arguments
3407 : * @return 0 ok, non-zero on error
3408 : */
3409 : int
3410 29 : main (int argc,
3411 : char *const *argv)
3412 : {
3413 : enum GNUNET_GenericReturnValue res;
3414 29 : struct GNUNET_GETOPT_CommandLineOption options[] = {
3415 29 : GNUNET_GETOPT_option_flag ('C',
3416 : "connection-close",
3417 : "force HTTP connections to be closed after each request",
3418 : &merchant_connection_close),
3419 29 : GNUNET_GETOPT_option_timetravel ('T',
3420 : "timetravel"),
3421 29 : GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION),
3422 : GNUNET_GETOPT_OPTION_END
3423 : };
3424 :
3425 29 : res = GNUNET_PROGRAM_run (
3426 : TALER_MERCHANT_project_data (),
3427 : argc, argv,
3428 : "taler-merchant-httpd",
3429 : "Taler merchant's HTTP backend interface",
3430 : options,
3431 : &run, NULL);
3432 29 : if (GNUNET_SYSERR == res)
3433 0 : return EXIT_INVALIDARGUMENT;
3434 29 : if (GNUNET_NO == res)
3435 14 : return EXIT_SUCCESS;
3436 15 : return global_ret;
3437 : }
|