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