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 src/backend/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 <donau/donau_service.h>
32 : #include "taler/taler_merchant_util.h"
33 : #include "taler-merchant-httpd_auth.h"
34 : #include "taler-merchant-httpd_dispatcher.h"
35 : #include "taler-merchant-httpd_exchanges.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_post-private-orders.h"
40 : #include "taler-merchant-httpd_post-orders-ORDER_ID-abort.h"
41 : #include "taler-merchant-httpd_post-challenge-ID.h"
42 : #include "taler-merchant-httpd_get-orders-ORDER_ID.h"
43 : #include "taler-merchant-httpd_get-sessions-SESSION_ID.h"
44 : #include "taler-merchant-httpd_get-templates-TEMPLATE_ID.h"
45 : #include "taler-merchant-httpd_get-exchanges.h"
46 : #include "taler-merchant-httpd_get-webui.h"
47 : #include "taler-merchant-httpd_get-terms.h"
48 : #include "taler-merchant-httpd_get-private-kyc.h"
49 : #include "taler-merchant-httpd_get-private-statistics-report-transactions.h"
50 : #include "taler-merchant-httpd_post-private-donau.h"
51 : #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h"
52 : #include "taler-merchant-httpd_get-private-orders.h"
53 : #include "taler-merchant-httpd_post-orders-ORDER_ID-pay.h"
54 : #include "taler-merchant-httpd_post-orders-ORDER_ID-refund.h"
55 : #include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
56 : #include "merchant-database/iterate_instances.h"
57 : #include "merchant-database/set_instance.h"
58 : #include "merchant-database/iterate_accounts_by_instance.h"
59 : #include "merchant-database/event_listen.h"
60 : #include "merchant-database/preflight.h"
61 : #include "merchant-database/event_notify.h"
62 :
63 : /**
64 : * Backlog for listen operation on unix-domain sockets.
65 : */
66 : #define UNIX_BACKLOG 500
67 :
68 : /**
69 : * Default maximum upload size permitted. Can be overridden
70 : * per handler.
71 : */
72 : #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024)
73 :
74 : char *TMH_currency;
75 :
76 : char *TMH_base_url;
77 :
78 : char *TMH_spa_dir;
79 :
80 : char *TMH_helper_email;
81 :
82 : char *TMH_helper_sms;
83 :
84 : char *TMH_phone_regex;
85 :
86 : regex_t TMH_phone_rx;
87 :
88 : char *TMH_allowed_payment_targets;
89 :
90 : char *TMH_default_persona;
91 :
92 : char *TMH_payment_target_regex;
93 :
94 : regex_t TMH_payment_target_re;
95 :
96 : int TMH_force_audit;
97 :
98 : struct TALER_MERCHANTDB_PostgresContext *TMH_db;
99 :
100 : struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map;
101 :
102 : struct GNUNET_TIME_Relative TMH_default_pay_delay;
103 :
104 : struct GNUNET_TIME_Relative TMH_default_refund_delay;
105 :
106 : struct GNUNET_TIME_Relative TMH_default_wire_transfer_delay;
107 :
108 : enum GNUNET_TIME_RounderInterval TMH_default_wire_transfer_rounding_interval;
109 :
110 : int TMH_strict_v19;
111 :
112 : int TMH_auth_disabled;
113 :
114 : int TMH_have_self_provisioning;
115 :
116 : int TMH_password_change_mfa;
117 :
118 : enum TEH_TanChannelSet TEH_mandatory_tan_channels;
119 :
120 : struct GNUNET_TIME_Relative TMH_legal_expiration;
121 :
122 : unsigned int TMH_num_cspecs;
123 :
124 : json_t *TMH_global_spa_config_data;
125 :
126 : struct TALER_CurrencySpecification *TMH_cspecs;
127 :
128 : struct GNUNET_CURL_Context *TMH_curl_ctx;
129 :
130 : /**
131 : * Event handler for instance settings changes.
132 : */
133 : static struct GNUNET_DB_EventHandler *instance_eh;
134 :
135 : /**
136 : * True if we started any HTTP daemon.
137 : */
138 : static bool have_daemons;
139 :
140 : /**
141 : * Should a "Connection: close" header be added to each HTTP response?
142 : */
143 : static int merchant_connection_close;
144 :
145 : /**
146 : * Should we enable HTTP/2 and HTTP/3 when talking to the exchange
147 : * (and donau)? Those are not expected to be terribly beneficial for
148 : * a server with stable connections to an exchange, but they could
149 : * cause stability issues with libcurl. Per default, we *enforce*
150 : * HTTP/1.x-only, as that is the conservative and most tested code
151 : * path.
152 : */
153 : static int enable_h3;
154 :
155 : /**
156 : * Context for integrating #TMH_curl_ctx with the
157 : * GNUnet event loop.
158 : */
159 : static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc;
160 :
161 : /**
162 : * Global return code
163 : */
164 : static int global_ret;
165 :
166 : /**
167 : * Our configuration.
168 : */
169 : const struct GNUNET_CONFIGURATION_Handle *TMH_cfg;
170 :
171 :
172 : void
173 73 : TMH_wire_method_free (struct TMH_WireMethod *wm)
174 : {
175 73 : GNUNET_free (wm->payto_uri.full_payto);
176 73 : GNUNET_free (wm->wire_method);
177 73 : GNUNET_free (wm->extra_wire_subject_metadata);
178 73 : GNUNET_free (wm->credit_facade_url);
179 73 : json_decref (wm->credit_facade_credentials);
180 73 : GNUNET_free (wm);
181 73 : }
182 :
183 :
184 : void
185 1215 : TMH_instance_decref (struct TMH_MerchantInstance *mi)
186 : {
187 : struct TMH_WireMethod *wm;
188 :
189 1215 : mi->rc--;
190 1215 : if (0 != mi->rc)
191 1092 : return;
192 123 : TMH_force_get_orders_resume (mi);
193 196 : while (NULL != (wm = (mi->wm_head)))
194 : {
195 73 : GNUNET_CONTAINER_DLL_remove (mi->wm_head,
196 : mi->wm_tail,
197 : wm);
198 73 : TMH_wire_method_free (wm);
199 : }
200 :
201 123 : GNUNET_free (mi->settings.id);
202 123 : GNUNET_free (mi->settings.name);
203 123 : GNUNET_free (mi->settings.email);
204 123 : GNUNET_free (mi->settings.phone);
205 123 : GNUNET_free (mi->settings.website);
206 123 : GNUNET_free (mi->settings.logo);
207 123 : json_decref (mi->settings.address);
208 123 : json_decref (mi->settings.jurisdiction);
209 123 : GNUNET_free (mi);
210 : }
211 :
212 :
213 : enum GNUNET_GenericReturnValue
214 123 : TMH_instance_free_cb (void *cls,
215 : const struct GNUNET_HashCode *key,
216 : void *value)
217 : {
218 123 : struct TMH_MerchantInstance *mi = value;
219 :
220 : (void) cls;
221 : (void) key;
222 123 : TMH_force_get_orders_resume (mi);
223 123 : GNUNET_assert (GNUNET_OK ==
224 : GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map,
225 : &mi->h_instance,
226 : mi));
227 123 : TMH_instance_decref (mi);
228 123 : return GNUNET_YES;
229 : }
230 :
231 :
232 : /**
233 : * Shutdown task (invoked when the application is being
234 : * terminated for any reason)
235 : *
236 : * @param cls NULL
237 : */
238 : static void
239 20 : do_shutdown (void *cls)
240 : {
241 : (void) cls;
242 20 : TALER_MHD_daemons_halt ();
243 20 : TMH_handler_statistic_report_transactions_cleanup ();
244 20 : TMH_force_kac_resume ();
245 20 : TMH_force_orders_resume ();
246 20 : TMH_force_get_sessions_ID_resume ();
247 20 : TMH_force_get_templates_ID_resume ();
248 20 : TMH_force_get_orders_resume_typst ();
249 20 : TMH_force_ac_resume ();
250 20 : TMH_force_pc_resume ();
251 20 : TMH_force_kyc_resume ();
252 20 : TMH_force_gorc_resume ();
253 20 : TMH_force_wallet_get_order_resume ();
254 20 : TMH_force_wallet_refund_order_resume ();
255 20 : TMH_challenge_done ();
256 20 : if (NULL != instance_eh)
257 : {
258 19 : TALER_MERCHANTDB_event_listen_cancel (instance_eh);
259 19 : instance_eh = NULL;
260 : }
261 20 : if (NULL != TMH_by_id_map)
262 : {
263 19 : GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
264 : &TMH_instance_free_cb,
265 : NULL);
266 19 : GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map);
267 19 : TMH_by_id_map = NULL;
268 : }
269 20 : TALER_MHD_daemons_destroy ();
270 20 : TMH_EXCHANGES_done ();
271 20 : if (NULL != TMH_db)
272 : {
273 19 : TALER_MERCHANTDB_disconnect (TMH_db);
274 19 : TMH_db = NULL;
275 : }
276 20 : TALER_TEMPLATING_done ();
277 20 : if (NULL != TMH_curl_ctx)
278 : {
279 20 : GNUNET_CURL_fini (TMH_curl_ctx);
280 20 : TMH_curl_ctx = NULL;
281 : }
282 20 : if (NULL != merchant_curl_rc)
283 : {
284 20 : GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
285 20 : merchant_curl_rc = NULL;
286 : }
287 20 : if (NULL != TMH_payment_target_regex)
288 : {
289 0 : regfree (&TMH_payment_target_re);
290 0 : GNUNET_free (TMH_payment_target_regex);
291 : }
292 20 : }
293 :
294 :
295 : /**
296 : * Function called whenever MHD is done with a request. If the
297 : * request was a POST, we may have stored a `struct Buffer *` in the
298 : * @a con_cls that might still need to be cleaned up. Call the
299 : * respective function to free the memory.
300 : *
301 : * @param cls client-defined closure
302 : * @param connection connection handle
303 : * @param con_cls value as set by the last call to
304 : * the #MHD_AccessHandlerCallback
305 : * @param toe reason for request termination
306 : * @see #MHD_OPTION_NOTIFY_COMPLETED
307 : * @ingroup request
308 : */
309 : static void
310 1143 : handle_mhd_completion_callback (void *cls,
311 : struct MHD_Connection *connection,
312 : void **con_cls,
313 : enum MHD_RequestTerminationCode toe)
314 : {
315 1143 : struct TMH_HandlerContext *hc = *con_cls;
316 :
317 : (void) cls;
318 1143 : if (NULL == hc)
319 0 : return;
320 1143 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
321 : {
322 : #if MHD_VERSION >= 0x00097304
323 : const union MHD_ConnectionInfo *ci;
324 1143 : unsigned int http_status = 0;
325 :
326 1143 : ci = MHD_get_connection_info (connection,
327 : MHD_CONNECTION_INFO_HTTP_STATUS);
328 1143 : if (NULL != ci)
329 1143 : http_status = ci->http_status;
330 1143 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
331 : "Request for `%s' completed with HTTP status %u (%d)\n",
332 : hc->url,
333 : http_status,
334 : toe);
335 : #else
336 : (void) connection;
337 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
338 : "Finished handling request for `%s' with MHD termination code %d\n",
339 : hc->url,
340 : (int) toe);
341 : #endif
342 : }
343 1143 : if (NULL != hc->cc)
344 393 : hc->cc (hc->ctx);
345 1143 : TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
346 1143 : GNUNET_free (hc->infix);
347 1143 : GNUNET_free (hc->rewritten_url);
348 1143 : if (NULL != hc->request_body)
349 729 : json_decref (hc->request_body);
350 1143 : if (NULL != hc->instance)
351 1091 : TMH_instance_decref (hc->instance);
352 1143 : TALER_MERCHANTDB_preflight (TMH_db);
353 1143 : GNUNET_free (hc->full_url);
354 1143 : GNUNET_free (hc);
355 1143 : *con_cls = NULL;
356 : }
357 :
358 :
359 : struct TMH_MerchantInstance *
360 1477 : TMH_lookup_instance (const char *instance_id)
361 : {
362 : struct GNUNET_HashCode h_instance;
363 : char *id;
364 :
365 1477 : if (NULL == instance_id)
366 1031 : id = GNUNET_strdup ("admin");
367 : else
368 446 : id = GNUNET_STRINGS_utf8_tolower (instance_id);
369 1477 : GNUNET_CRYPTO_hash (id,
370 : strlen (id),
371 : &h_instance);
372 1477 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373 : "Looking for by-id key %s of '%s' in hashmap\n",
374 : GNUNET_h2s (&h_instance),
375 : id);
376 1477 : GNUNET_free (id);
377 : /* We're fine if that returns NULL, the calling routine knows how
378 : to handle that */
379 1477 : return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map,
380 : &h_instance);
381 : }
382 :
383 :
384 : /**
385 : * Add instance definition to our active set of instances.
386 : *
387 : * @param[in,out] mi merchant instance details to define
388 : * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already
389 : */
390 : enum GNUNET_GenericReturnValue
391 123 : TMH_add_instance (struct TMH_MerchantInstance *mi)
392 : {
393 : const char *id;
394 : enum GNUNET_GenericReturnValue ret;
395 :
396 123 : id = mi->settings.id;
397 123 : if (NULL == id)
398 0 : id = "admin";
399 123 : GNUNET_CRYPTO_hash (id,
400 : strlen (id),
401 : &mi->h_instance);
402 123 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403 : "Looking for by-id key %s of `%s' in hashmap\n",
404 : GNUNET_h2s (&mi->h_instance),
405 : id);
406 123 : ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map,
407 123 : &mi->h_instance,
408 : mi,
409 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
410 123 : if (GNUNET_OK == ret)
411 : {
412 123 : GNUNET_assert (mi->rc < UINT_MAX);
413 123 : mi->rc++;
414 : }
415 123 : return ret;
416 : }
417 :
418 :
419 : /**
420 : * Function called first by MHD with the full URL.
421 : *
422 : * @param cls NULL
423 : * @param full_url the full URL
424 : * @param con MHD connection object
425 : * @return our handler context
426 : */
427 : static void *
428 1143 : full_url_track_callback (void *cls,
429 : const char *full_url,
430 : struct MHD_Connection *con)
431 : {
432 : struct TMH_HandlerContext *hc;
433 :
434 1143 : hc = GNUNET_new (struct TMH_HandlerContext);
435 1143 : hc->connection = con;
436 1143 : GNUNET_async_scope_fresh (&hc->async_scope_id);
437 1143 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
438 1143 : hc->full_url = GNUNET_strdup (full_url);
439 1143 : return hc;
440 : }
441 :
442 :
443 : /**
444 : * The callback was called again by MHD, continue processing
445 : * the request with the already identified handler.
446 : *
447 : * @param hc the handler context
448 : * @param upload_data the data being uploaded (excluding HEADERS,
449 : * for a POST that fits into memory and that is encoded
450 : * with a supported encoding, the POST data will NOT be
451 : * given in upload_data and is instead available as
452 : * part of #MHD_get_connection_values; very large POST
453 : * data *will* be made available incrementally in
454 : * @a upload_data)
455 : * @param upload_data_size set initially to the size of the
456 : * @a upload_data provided; the method must update this
457 : * value to the number of bytes NOT processed;
458 : * @return #MHD_YES if the connection was handled successfully,
459 : * #MHD_NO if the socket must be closed due to a serious
460 : * error while handling the request
461 : */
462 : static enum MHD_Result
463 2024 : process_upload_with_handler (struct TMH_HandlerContext *hc,
464 : const char *upload_data,
465 : size_t *upload_data_size)
466 : {
467 2024 : GNUNET_assert (NULL != hc->rh);
468 2024 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
469 2024 : if ( (hc->has_body) &&
470 1615 : (NULL == hc->request_body) )
471 : {
472 1458 : size_t mul = hc->rh->max_upload;
473 : enum GNUNET_GenericReturnValue res;
474 :
475 1458 : if (0 == mul)
476 36 : mul = DEFAULT_MAX_UPLOAD_SIZE;
477 1458 : if ( (hc->total_upload + *upload_data_size < hc->total_upload) ||
478 1458 : (hc->total_upload + *upload_data_size > mul) )
479 : {
480 : /* Client exceeds upload limit. Should _usually_ be checked earlier
481 : when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with
482 : chunked encoding an uploader MAY have omitted this, and thus
483 : not permitted us to check on time. In this case, we just close
484 : the connection once it exceeds our limit (instead of waiting
485 : for the upload to complete and then fail). This could theoretically
486 : cause some clients to retry, alas broken or malicious clients
487 : are likely to retry anyway, so little we can do about it, and
488 : failing earlier seems the best option here. */
489 0 : GNUNET_break_op (0);
490 0 : return MHD_NO;
491 : }
492 1458 : hc->total_upload += *upload_data_size;
493 1458 : res = TALER_MHD_parse_post_json (hc->connection,
494 : &hc->json_parse_context,
495 : upload_data,
496 : upload_data_size,
497 : &hc->request_body);
498 1458 : if (GNUNET_SYSERR == res)
499 0 : return MHD_NO;
500 : /* A error response was already generated */
501 1458 : if ( (GNUNET_NO == res) ||
502 : /* or, need more data to accomplish parsing */
503 1458 : (NULL == hc->request_body) )
504 729 : return MHD_YES; /* let MHD call us *again* */
505 : }
506 : /* Upload complete (if any), call handler to generate reply */
507 1295 : return hc->rh->handler (hc->rh,
508 : hc->connection,
509 : hc);
510 : }
511 :
512 :
513 : /**
514 : * Log information about the request being handled.
515 : *
516 : * @param hc handler context
517 : * @param method HTTP method of the request
518 : */
519 : static void
520 1143 : log_request (const struct TMH_HandlerContext *hc,
521 : const char *method)
522 : {
523 : const char *correlation_id;
524 :
525 1143 : correlation_id = MHD_lookup_connection_value (hc->connection,
526 : MHD_HEADER_KIND,
527 : "Taler-Correlation-Id");
528 1143 : if ( (NULL != correlation_id) &&
529 : (GNUNET_YES !=
530 0 : GNUNET_CURL_is_valid_scope_id (correlation_id)) )
531 : {
532 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
533 : "Illegal incoming correlation ID\n");
534 0 : correlation_id = NULL;
535 : }
536 1143 : if (NULL != correlation_id)
537 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
538 : "Handling request for (%s) URL '%s', correlation_id=%s\n",
539 : method,
540 : hc->url,
541 : correlation_id);
542 : else
543 1143 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
544 : "Handling request (%s) for URL '%s'\n",
545 : method,
546 : hc->url);
547 1143 : }
548 :
549 :
550 : /**
551 : * Identify the instance of the request from the URL.
552 : *
553 : * @param[in,out] hc handler context
554 : * @param[in,out] urlp URL path of the request, updated to point to the rest
555 : * @param[out] use_admin set to true if we are using the admin instance
556 : * @return #GNUNET_OK on success,
557 : * #GNUNET_NO if an error was queued (return #MHD_YES)
558 : * #GNUNET_SYSERR to close the connection (return #MHD_NO)
559 : */
560 : static enum GNUNET_GenericReturnValue
561 1143 : identify_instance (struct TMH_HandlerContext *hc,
562 : const char **urlp,
563 : bool *use_admin)
564 : {
565 1143 : const char *url = *urlp;
566 1143 : const char *instance_prefix = "/instances/";
567 :
568 1143 : if (0 == strncmp (url,
569 : instance_prefix,
570 : strlen (instance_prefix)))
571 : {
572 : /* url starts with "/instances/" */
573 112 : const char *istart = url + strlen (instance_prefix);
574 112 : const char *slash = strchr (istart, '/');
575 : char *raw_id;
576 : char *instance_id;
577 :
578 112 : if (NULL == slash)
579 0 : raw_id = GNUNET_strdup (istart);
580 : else
581 112 : raw_id = GNUNET_strndup (istart,
582 : slash - istart);
583 : /* Instance IDs are case-insensitive, so fold the segment before comparing
584 : it against "admin" below. Without this, '/instances/Admin/' misses the
585 : redirect to the modern path, leaving use_admin false, and every
586 : 'default_only' handler that '/instances/admin/' reaches (all of
587 : /management/) then replies 404. */
588 112 : instance_id = GNUNET_STRINGS_utf8_tolower (raw_id);
589 112 : GNUNET_free (raw_id);
590 112 : if (0 == strcmp (instance_id,
591 : "admin"))
592 : {
593 : enum MHD_Result ret;
594 : struct MHD_Response *response;
595 0 : const char *rstart = hc->full_url + strlen (instance_prefix);
596 0 : const char *rslash = strchr (rstart, '/');
597 :
598 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
599 : "Client used deprecated '/instances/admin/' path. Redirecting to modern path\n");
600 :
601 : response
602 0 : = MHD_create_response_from_buffer (0,
603 : NULL,
604 : MHD_RESPMEM_PERSISTENT);
605 0 : TALER_MHD_add_global_headers (response,
606 : true);
607 0 : if (MHD_NO ==
608 0 : MHD_add_response_header (response,
609 : MHD_HTTP_HEADER_LOCATION,
610 : NULL == rslash
611 : ? "/"
612 : : rslash))
613 : {
614 0 : GNUNET_break (0);
615 0 : MHD_destroy_response (response);
616 0 : GNUNET_free (instance_id);
617 0 : return GNUNET_SYSERR;
618 : }
619 0 : ret = MHD_queue_response (hc->connection,
620 : MHD_HTTP_PERMANENT_REDIRECT,
621 : response);
622 0 : MHD_destroy_response (response);
623 0 : GNUNET_free (instance_id);
624 0 : return (MHD_YES == ret) ? GNUNET_NO : GNUNET_SYSERR;
625 : }
626 112 : hc->instance = TMH_lookup_instance (instance_id);
627 112 : if ( (NULL == hc->instance) &&
628 2 : (0 == strcmp ("admin",
629 : instance_id)) )
630 0 : hc->instance = TMH_lookup_instance (NULL);
631 112 : GNUNET_free (instance_id);
632 112 : if (NULL == slash)
633 0 : *urlp = "";
634 : else
635 112 : *urlp = slash;
636 : }
637 : else
638 : {
639 : /* use 'default' */
640 1031 : *use_admin = true;
641 1031 : hc->instance = TMH_lookup_instance (NULL);
642 : }
643 1143 : if (NULL != hc->instance)
644 : {
645 1091 : GNUNET_assert (hc->instance->rc < UINT_MAX);
646 1091 : hc->instance->rc++;
647 : }
648 1143 : return GNUNET_OK;
649 : }
650 :
651 :
652 : /**
653 : * A client has requested the given url using the given method
654 : * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
655 : * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
656 : * must call MHD callbacks to provide content to give back to the
657 : * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
658 : * #MHD_HTTP_NOT_FOUND, etc.).
659 : *
660 : * @param cls argument given together with the function
661 : * pointer when the handler was registered with MHD
662 : * @param connection the MHD connection to handle
663 : * @param url the requested url
664 : * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
665 : * #MHD_HTTP_METHOD_PUT, etc.)
666 : * @param version the HTTP version string (i.e.
667 : * #MHD_HTTP_VERSION_1_1)
668 : * @param upload_data the data being uploaded (excluding HEADERS,
669 : * for a POST that fits into memory and that is encoded
670 : * with a supported encoding, the POST data will NOT be
671 : * given in upload_data and is instead available as
672 : * part of #MHD_get_connection_values; very large POST
673 : * data *will* be made available incrementally in
674 : * @a upload_data)
675 : * @param upload_data_size set initially to the size of the
676 : * @a upload_data provided; the method must update this
677 : * value to the number of bytes NOT processed;
678 : * @param con_cls pointer that the callback can set to some
679 : * address and that will be preserved by MHD for future
680 : * calls for this request; since the access handler may
681 : * be called many times (i.e., for a PUT/POST operation
682 : * with plenty of upload data) this allows the application
683 : * to easily associate some request-specific state.
684 : * If necessary, this state can be cleaned up in the
685 : * global #MHD_RequestCompletedCallback (which
686 : * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
687 : * Initially, `*con_cls` will be set up by the
688 : * full_url_track_callback().
689 : * @return #MHD_YES if the connection was handled successfully,
690 : * #MHD_NO if the socket must be closed due to a serious
691 : * error while handling the request
692 : */
693 : static enum MHD_Result
694 3167 : url_handler (void *cls,
695 : struct MHD_Connection *connection,
696 : const char *url,
697 : const char *method,
698 : const char *version,
699 : const char *upload_data,
700 : size_t *upload_data_size,
701 : void **con_cls)
702 : {
703 3167 : struct TMH_HandlerContext *hc = *con_cls;
704 3167 : bool use_admin = false;
705 3167 : bool is_public = false;
706 :
707 : (void) cls;
708 : (void) version;
709 3167 : if (NULL == hc->url)
710 : {
711 : /* First time.
712 : * Find out the merchant backend instance for the request.
713 : * If there is an instance, remove the instance specification
714 : * from the beginning of the request URL. */
715 : enum GNUNET_GenericReturnValue ret;
716 :
717 1143 : hc->url = url;
718 1143 : log_request (hc,
719 : method);
720 1143 : ret = identify_instance (hc,
721 : &url,
722 : &use_admin);
723 1143 : if (GNUNET_OK != ret)
724 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
725 : }
726 :
727 3167 : if (NULL != hc->instance)
728 : {
729 : /* Narrow DB interaction to selected instance */
730 : enum GNUNET_DB_QueryStatus qs;
731 :
732 3047 : qs = TALER_MERCHANTDB_set_instance (TMH_db,
733 3047 : hc->instance->settings.id);
734 3047 : switch (qs)
735 : {
736 0 : case GNUNET_DB_STATUS_HARD_ERROR:
737 0 : GNUNET_break (0);
738 0 : return TALER_MHD_reply_with_error (
739 : connection,
740 : MHD_HTTP_INTERNAL_SERVER_ERROR,
741 : TALER_EC_GENERIC_DB_SETUP_FAILED,
742 : "set_instance");
743 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
744 0 : GNUNET_break (0);
745 0 : return TALER_MHD_reply_with_error (
746 : connection,
747 : MHD_HTTP_INTERNAL_SERVER_ERROR,
748 : TALER_EC_GENERIC_DB_SETUP_FAILED,
749 : "set_instance");
750 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
751 0 : return TALER_MHD_reply_with_error (
752 : connection,
753 : MHD_HTTP_NOT_FOUND,
754 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
755 : hc->url);
756 3047 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
757 3047 : break;
758 : }
759 : }
760 :
761 3167 : if (NULL != hc->rh)
762 : {
763 : enum MHD_Result res;
764 :
765 : /* MHD calls us again for a request, we already identified
766 : the handler, just continue processing with the handler */
767 2024 : res = process_upload_with_handler (hc,
768 : upload_data,
769 : upload_data_size);
770 2024 : if (NULL != hc->instance)
771 : {
772 1956 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
773 : TALER_MERCHANTDB_set_instance (TMH_db,
774 : NULL));
775 : }
776 2024 : return res;
777 : }
778 :
779 : /* First time, let's figure out the handler */
780 : {
781 : enum GNUNET_GenericReturnValue ret;
782 :
783 1143 : ret = TMH_dispatch_request (hc,
784 : url,
785 : method,
786 : use_admin,
787 : &is_public);
788 1143 : if (GNUNET_OK != ret)
789 : {
790 1 : if (NULL != hc->instance)
791 : {
792 0 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
793 : TALER_MERCHANTDB_set_instance (TMH_db,
794 : NULL));
795 : }
796 1 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
797 : }
798 : }
799 :
800 : /* At this point, we must have found a handler */
801 1142 : GNUNET_assert (NULL != hc->rh);
802 :
803 : /* If an instance must be there, check one exists */
804 1142 : if ( (NULL == hc->instance) &&
805 51 : (! hc->rh->skip_instance) )
806 : {
807 3 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
808 : "Instance for `%s' not known\n",
809 : hc->url);
810 3 : return TALER_MHD_reply_with_error (connection,
811 : MHD_HTTP_NOT_FOUND,
812 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
813 : hc->url);
814 : }
815 :
816 : /* Perform access control for non-public handlers */
817 1139 : if (! is_public)
818 : {
819 : enum GNUNET_GenericReturnValue ret;
820 :
821 819 : ret = TMH_perform_access_control (hc);
822 819 : if (GNUNET_OK != ret)
823 : {
824 24 : if (NULL != hc->instance)
825 : {
826 22 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
827 : TALER_MERCHANTDB_set_instance (TMH_db,
828 : NULL));
829 : }
830 24 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
831 : }
832 : }
833 1115 : if (NULL != hc->instance)
834 : {
835 1069 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
836 : TALER_MERCHANTDB_set_instance (TMH_db,
837 : NULL));
838 : }
839 :
840 1115 : if ( (NULL != hc->instance) && /* make static analysis happy */
841 1069 : (! hc->rh->skip_instance) &&
842 1021 : (hc->instance->deleted) &&
843 3 : (! hc->rh->allow_deleted_instance) )
844 : {
845 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
846 : "Instance `%s' was deleted\n",
847 : hc->instance->settings.id);
848 3 : return TALER_MHD_reply_with_error (connection,
849 : MHD_HTTP_NOT_FOUND,
850 : TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED,
851 3 : hc->instance->settings.id);
852 : }
853 :
854 : /* Check upload constraints */
855 2224 : hc->has_body = ( (0 == strcasecmp (method,
856 1562 : MHD_HTTP_METHOD_POST)) ||
857 : /* PUT is not yet used */
858 450 : (0 == strcasecmp (method,
859 1112 : MHD_HTTP_METHOD_PATCH)) );
860 1112 : if (hc->has_body)
861 : {
862 : /* This is a macro: it will queue an error response and return
863 : from this function if the upload would be too large. */
864 729 : TALER_MHD_check_content_length (connection,
865 : 0 == hc->rh->max_upload
866 : ? DEFAULT_MAX_UPLOAD_SIZE
867 : : hc->rh->max_upload);
868 729 : GNUNET_break (NULL == hc->request_body); /* can't have it already */
869 : }
870 : /* wait for MHD to call us again, this time hc->url will be non-NULL
871 : and we should jump straight into process_upload_with_handler(). */
872 1112 : return MHD_YES;
873 : }
874 :
875 :
876 : /**
877 : * Callback invoked with information about a bank account.
878 : *
879 : * @param cls closure with a `struct TMH_MerchantInstance *`
880 : * @param merchant_priv private key of the merchant instance
881 : * @param acc details about the account
882 : */
883 : static void
884 46 : add_account_cb (void *cls,
885 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
886 : const struct TALER_MERCHANTDB_AccountDetails *acc)
887 : {
888 46 : struct TMH_MerchantInstance *mi = cls;
889 : struct TMH_WireMethod *wm;
890 :
891 : (void) merchant_priv;
892 46 : wm = GNUNET_new (struct TMH_WireMethod);
893 46 : wm->h_wire = acc->h_wire;
894 : wm->payto_uri.full_payto
895 46 : = GNUNET_strdup (acc->payto_uri.full_payto);
896 46 : if (NULL != acc->extra_wire_subject_metadata)
897 : wm->extra_wire_subject_metadata
898 0 : = GNUNET_strdup (acc->extra_wire_subject_metadata);
899 46 : wm->wire_salt = acc->salt;
900 : wm->wire_method
901 46 : = TALER_payto_get_method (acc->payto_uri.full_payto);
902 46 : wm->active = acc->active;
903 46 : GNUNET_CONTAINER_DLL_insert (mi->wm_head,
904 : mi->wm_tail,
905 : wm);
906 46 : }
907 :
908 :
909 : /**
910 : * Function called during startup to add all known instances to our
911 : * hash map in memory for faster lookups when we receive requests.
912 : *
913 : * @param cls closure, NULL, unused
914 : * @param merchant_pub public key of the instance
915 : * @param merchant_priv private key of the instance, NULL if not available
916 : * @param is detailed configuration settings for the instance
917 : * @param ias authentication settings for the instance
918 : */
919 : static void
920 86 : add_instance_cb (void *cls,
921 : const struct TALER_MerchantPublicKeyP *merchant_pub,
922 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
923 : const struct TALER_MERCHANTDB_InstanceSettings *is,
924 : const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
925 : {
926 : struct TMH_MerchantInstance *mi;
927 : enum GNUNET_DB_QueryStatus qs;
928 :
929 : (void) cls;
930 86 : mi = TMH_lookup_instance (is->id);
931 86 : if (NULL != mi)
932 : {
933 : /* (outdated) entry exists, remove old entry */
934 0 : (void) TMH_instance_free_cb (NULL,
935 0 : &mi->h_instance,
936 : mi);
937 : }
938 86 : mi = GNUNET_new (struct TMH_MerchantInstance);
939 86 : mi->settings = *is;
940 86 : mi->auth = *ias;
941 86 : mi->settings.id = GNUNET_STRINGS_utf8_tolower (mi->settings.id);
942 86 : mi->settings.name = GNUNET_strdup (mi->settings.name);
943 86 : if (NULL != mi->settings.email)
944 7 : mi->settings.email = GNUNET_strdup (mi->settings.email);
945 86 : if (NULL != mi->settings.phone)
946 7 : mi->settings.phone = GNUNET_strdup (mi->settings.phone);
947 86 : if (NULL != mi->settings.website)
948 0 : mi->settings.website = GNUNET_strdup (mi->settings.website);
949 86 : if (NULL != mi->settings.logo)
950 0 : mi->settings.logo = GNUNET_strdup (mi->settings.logo);
951 86 : mi->settings.address = json_incref (mi->settings.address);
952 86 : mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
953 86 : if (NULL != merchant_priv)
954 82 : mi->merchant_priv = *merchant_priv;
955 : else
956 4 : mi->deleted = true;
957 86 : mi->merchant_pub = *merchant_pub;
958 86 : qs = TALER_MERCHANTDB_set_instance (TMH_db,
959 86 : mi->settings.id);
960 86 : if (0 > qs)
961 : {
962 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
963 : "Error setting instance `%s'\n",
964 : mi->settings.id);
965 0 : GNUNET_SCHEDULER_shutdown ();
966 0 : return;
967 : }
968 86 : qs = TALER_MERCHANTDB_iterate_accounts_by_instance (
969 : TMH_db,
970 86 : mi->settings.id,
971 : &add_account_cb,
972 : mi);
973 86 : if (0 > qs)
974 : {
975 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
976 : "Error loading accounts of `%s' from database\n",
977 : mi->settings.id);
978 0 : GNUNET_SCHEDULER_shutdown ();
979 0 : return;
980 : }
981 86 : GNUNET_assert (GNUNET_OK ==
982 : TMH_add_instance (mi));
983 86 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
984 : TALER_MERCHANTDB_set_instance (TMH_db,
985 : NULL));
986 : }
987 :
988 :
989 : /**
990 : * Trigger (re)loading of instance settings from DB.
991 : *
992 : * @param cls NULL
993 : * @param extra ID of the instance that changed, NULL
994 : * to load all instances (will not handle purges!)
995 : * @param extra_len number of bytes in @a extra
996 : */
997 : static void
998 117 : load_instances (void *cls,
999 : const void *extra,
1000 : size_t extra_len)
1001 : {
1002 : enum GNUNET_DB_QueryStatus qs;
1003 117 : const char *id = extra;
1004 :
1005 : (void) cls;
1006 117 : if ( (NULL != extra) &&
1007 98 : ( (0 == extra_len) ||
1008 98 : ('\0' != id[extra_len - 1]) ) )
1009 : {
1010 0 : GNUNET_break (0 == extra_len);
1011 0 : extra = NULL;
1012 : }
1013 117 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1014 : "Received instance settings notification: reload `%s'\n",
1015 : id);
1016 117 : if (NULL == extra)
1017 : {
1018 19 : qs = TALER_MERCHANTDB_iterate_instances (TMH_db,
1019 : false,
1020 : &add_instance_cb,
1021 : NULL);
1022 : }
1023 : else
1024 : {
1025 : struct TMH_MerchantInstance *mi;
1026 :
1027 : /* This must be done here to handle instance
1028 : purging, as for purged instances, the DB
1029 : lookup below will otherwise do nothing */
1030 98 : mi = TMH_lookup_instance (id);
1031 98 : if (NULL != mi)
1032 : {
1033 98 : (void) TMH_instance_free_cb (NULL,
1034 98 : &mi->h_instance,
1035 : mi);
1036 : }
1037 98 : qs = TALER_MERCHANTDB_iterate_instances_by_id (TMH_db,
1038 : id,
1039 : false,
1040 : &add_instance_cb,
1041 : NULL);
1042 : }
1043 117 : if (0 > qs)
1044 : {
1045 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1046 : "Failed initialization. Check database setup.\n");
1047 0 : global_ret = EXIT_FAILURE;
1048 0 : GNUNET_SCHEDULER_shutdown ();
1049 0 : return;
1050 : }
1051 : }
1052 :
1053 :
1054 : /**
1055 : * A transaction modified an instance setting (or created/deleted/purged
1056 : * one). Notify all backends about the change.
1057 : *
1058 : * @param id ID of the instance that changed
1059 : */
1060 : void
1061 98 : TMH_reload_instances (const char *id)
1062 : {
1063 98 : struct GNUNET_DB_EventHeaderP es = {
1064 98 : .size = htons (sizeof (es)),
1065 98 : .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
1066 : };
1067 :
1068 98 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1069 : "Generating instance settings notification: reload `%s'\n",
1070 : id);
1071 196 : TALER_MERCHANTDB_event_notify (TMH_db,
1072 : &es,
1073 : id,
1074 : (NULL == id)
1075 : ? 0
1076 98 : : strlen (id) + 1);
1077 98 : }
1078 :
1079 :
1080 : /**
1081 : * Callback invoked on every listen socket to start the
1082 : * respective MHD HTTP daemon.
1083 : *
1084 : * @param cls unused
1085 : * @param lsock the listen socket
1086 : */
1087 : static void
1088 38 : start_daemon (void *cls,
1089 : int lsock)
1090 : {
1091 : struct MHD_Daemon *mhd;
1092 :
1093 : (void) cls;
1094 38 : GNUNET_assert (-1 != lsock);
1095 38 : mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK
1096 : | MHD_USE_AUTO,
1097 : 0 /* port */,
1098 : NULL, NULL,
1099 : &url_handler, NULL,
1100 : MHD_OPTION_LISTEN_SOCKET, lsock,
1101 : MHD_OPTION_URI_LOG_CALLBACK,
1102 : &full_url_track_callback, NULL,
1103 : MHD_OPTION_NOTIFY_COMPLETED,
1104 : &handle_mhd_completion_callback, NULL,
1105 : MHD_OPTION_CONNECTION_TIMEOUT,
1106 : (unsigned int) 10 /* 10s */,
1107 : MHD_OPTION_END);
1108 38 : if (NULL == mhd)
1109 : {
1110 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1111 : "Failed to launch HTTP service.\n");
1112 0 : GNUNET_SCHEDULER_shutdown ();
1113 0 : return;
1114 : }
1115 38 : have_daemons = true;
1116 38 : TALER_MHD_daemon_start (mhd);
1117 : }
1118 :
1119 :
1120 : /**
1121 : * Main function that will be run by the scheduler.
1122 : *
1123 : * @param cls closure
1124 : * @param args remaining command-line arguments
1125 : * @param cfgfile name of the configuration file used (for saving, can be
1126 : * NULL!)
1127 : * @param config configuration
1128 : */
1129 : static void
1130 20 : run (void *cls,
1131 : char *const *args,
1132 : const char *cfgfile,
1133 : const struct GNUNET_CONFIGURATION_Handle *config)
1134 : {
1135 : enum TALER_MHD_GlobalOptions go;
1136 : int elen;
1137 :
1138 : (void) cls;
1139 : (void) args;
1140 : (void) cfgfile;
1141 20 : TMH_cfg = config;
1142 20 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1143 : "Starting taler-merchant-httpd\n");
1144 20 : go = TALER_MHD_GO_NONE;
1145 20 : if (merchant_connection_close)
1146 0 : go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
1147 20 : TALER_MHD_setup (go);
1148 20 : TALER_EXCHANGE_setup (enable_h3
1149 20 : ? TALER_EXCHANGE_GO_ENABLE_HTTP3
1150 : : TALER_EXCHANGE_GO_FORCE_HTTP1_1);
1151 20 : DONAU_setup (enable_h3
1152 20 : ? DONAU_GO_ENABLE_HTTP3
1153 : : DONAU_GO_FORCE_HTTP1_1);
1154 :
1155 20 : global_ret = EXIT_SUCCESS;
1156 20 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1157 : NULL);
1158 :
1159 : TMH_curl_ctx
1160 20 : = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1161 : &merchant_curl_rc);
1162 20 : if (NULL == TMH_curl_ctx)
1163 : {
1164 0 : GNUNET_break (0);
1165 0 : global_ret = EXIT_FAILURE;
1166 0 : GNUNET_SCHEDULER_shutdown ();
1167 0 : return;
1168 : }
1169 20 : merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx);
1170 : /* Disable 100 continue processing */
1171 20 : GNUNET_break (GNUNET_OK ==
1172 : GNUNET_CURL_append_header (TMH_curl_ctx,
1173 : MHD_HTTP_HEADER_EXPECT ":"));
1174 20 : GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx,
1175 : "Taler-Correlation-Id");
1176 :
1177 20 : if (GNUNET_SYSERR ==
1178 20 : TALER_config_get_currency (TMH_cfg,
1179 : "merchant",
1180 : &TMH_currency))
1181 : {
1182 0 : global_ret = EXIT_NOTCONFIGURED;
1183 0 : GNUNET_SCHEDULER_shutdown ();
1184 0 : return;
1185 : }
1186 20 : if (GNUNET_OK !=
1187 20 : TALER_CONFIG_parse_currencies (TMH_cfg,
1188 : TMH_currency,
1189 : &TMH_num_cspecs,
1190 : &TMH_cspecs))
1191 : {
1192 0 : global_ret = EXIT_NOTCONFIGURED;
1193 0 : GNUNET_SCHEDULER_shutdown ();
1194 0 : return;
1195 : }
1196 :
1197 : {
1198 : char *spa_data;
1199 :
1200 20 : if (GNUNET_OK ==
1201 20 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1202 : "merchant",
1203 : "GLOBAL_SPA_CONFIG_DATA",
1204 : &spa_data))
1205 : {
1206 : json_error_t err;
1207 :
1208 0 : TMH_global_spa_config_data = json_loads (spa_data,
1209 : JSON_REJECT_DUPLICATES,
1210 : &err);
1211 0 : GNUNET_free (spa_data);
1212 0 : if (NULL == TMH_global_spa_config_data)
1213 : {
1214 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1215 : "merchant",
1216 : "GLOBAL_SPA_CONFIG_DATA",
1217 : err.text);
1218 0 : global_ret = EXIT_NOTCONFIGURED;
1219 0 : GNUNET_SCHEDULER_shutdown ();
1220 0 : return;
1221 : }
1222 : }
1223 : }
1224 :
1225 :
1226 20 : if (GNUNET_SYSERR ==
1227 : (TMH_strict_v19
1228 20 : = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
1229 : "merchant",
1230 : "STRICT_PROTOCOL_V19")))
1231 : {
1232 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1233 : "merchant",
1234 : "STRICT_PROTOCOL_V19");
1235 0 : TMH_strict_v19 = GNUNET_NO;
1236 : }
1237 20 : if (GNUNET_SYSERR ==
1238 20 : (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
1239 : "merchant",
1240 : "DISABLE_AUTHENTICATION")))
1241 : {
1242 0 : TMH_auth_disabled = GNUNET_NO;
1243 : }
1244 20 : if (GNUNET_YES == TMH_auth_disabled)
1245 : {
1246 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1247 : "DANGEROUS: Endpoint Authentication disabled!");
1248 : }
1249 :
1250 20 : if (GNUNET_SYSERR ==
1251 : (TMH_have_self_provisioning
1252 20 : = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
1253 : "merchant",
1254 : "ENABLE_SELF_PROVISIONING")))
1255 : {
1256 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1257 : "merchant",
1258 : "ENABLE_SELF_PROVISIONING");
1259 0 : TMH_have_self_provisioning = GNUNET_NO;
1260 : }
1261 :
1262 20 : if (GNUNET_SYSERR ==
1263 : (TMH_password_change_mfa
1264 20 : = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
1265 : "merchant",
1266 : "PASSWORD_CHANGE_MFA")))
1267 : {
1268 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1269 : "merchant",
1270 : "PASSWORD_CHANGE_MFA");
1271 0 : TMH_password_change_mfa = GNUNET_NO;
1272 : }
1273 :
1274 20 : if (GNUNET_OK !=
1275 20 : GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
1276 : "merchant",
1277 : "LEGAL_PRESERVATION",
1278 : &TMH_legal_expiration))
1279 : {
1280 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1281 : "merchant",
1282 : "LEGAL_PRESERVATION");
1283 0 : global_ret = EXIT_NOTCONFIGURED;
1284 0 : GNUNET_SCHEDULER_shutdown ();
1285 0 : return;
1286 : }
1287 :
1288 20 : if (GNUNET_OK !=
1289 20 : GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
1290 : "merchant",
1291 : "DEFAULT_PAY_DELAY",
1292 : &TMH_default_pay_delay))
1293 : {
1294 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1295 : "merchant",
1296 : "DEFAULT_PAY_DELAY");
1297 0 : TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS;
1298 : }
1299 20 : if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay))
1300 : {
1301 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
1302 : "merchant",
1303 : "DEFAULT_PAY_DELAY",
1304 : "forever is not allowed");
1305 0 : global_ret = EXIT_NOTCONFIGURED;
1306 0 : GNUNET_SCHEDULER_shutdown ();
1307 0 : return;
1308 : }
1309 20 : if (GNUNET_OK !=
1310 20 : GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
1311 : "merchant",
1312 : "DEFAULT_REFUND_DELAY",
1313 : &TMH_default_refund_delay))
1314 : {
1315 20 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1316 : "merchant",
1317 : "DEFAULT_REFUND_DELAY");
1318 20 : TMH_default_refund_delay = GNUNET_TIME_relative_multiply (
1319 : GNUNET_TIME_UNIT_DAYS,
1320 : 15);
1321 : }
1322 20 : if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay))
1323 : {
1324 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
1325 : "merchant",
1326 : "DEFAULT_REFUND_DELAY",
1327 : "forever is not allowed");
1328 0 : global_ret = EXIT_NOTCONFIGURED;
1329 0 : GNUNET_SCHEDULER_shutdown ();
1330 0 : return;
1331 : }
1332 :
1333 20 : if (GNUNET_OK !=
1334 20 : GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
1335 : "merchant",
1336 : "DEFAULT_WIRE_TRANSFER_DELAY",
1337 : &TMH_default_wire_transfer_delay))
1338 : {
1339 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1340 : "merchant",
1341 : "DEFAULT_WIRE_TRANSFER_DELAY");
1342 0 : TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS;
1343 : }
1344 20 : if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay))
1345 : {
1346 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
1347 : "merchant",
1348 : "DEFAULT_WIRE_TRANSFER_DELAY",
1349 : "forever is not allowed");
1350 0 : global_ret = EXIT_NOTCONFIGURED;
1351 0 : GNUNET_SCHEDULER_shutdown ();
1352 0 : return;
1353 : }
1354 :
1355 : {
1356 : char *dwtri;
1357 :
1358 20 : if (GNUNET_OK !=
1359 20 : GNUNET_CONFIGURATION_get_value_string (
1360 : TMH_cfg,
1361 : "merchant",
1362 : "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
1363 : &dwtri))
1364 : {
1365 20 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1366 : "merchant",
1367 : "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL");
1368 20 : TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE;
1369 : }
1370 : else
1371 : {
1372 0 : if (GNUNET_OK !=
1373 0 : GNUNET_TIME_string_to_round_interval (
1374 : dwtri,
1375 : &TMH_default_wire_transfer_rounding_interval))
1376 : {
1377 0 : GNUNET_log_config_invalid (
1378 : GNUNET_ERROR_TYPE_ERROR,
1379 : "merchant",
1380 : "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
1381 : "invalid time rounding interval");
1382 0 : global_ret = EXIT_NOTCONFIGURED;
1383 0 : GNUNET_free (dwtri);
1384 0 : GNUNET_SCHEDULER_shutdown ();
1385 0 : return;
1386 : }
1387 0 : GNUNET_free (dwtri);
1388 : }
1389 : }
1390 :
1391 20 : TMH_load_terms (TMH_cfg);
1392 :
1393 20 : if (GNUNET_OK !=
1394 20 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1395 : "merchant",
1396 : "PAYMENT_TARGET_TYPES",
1397 : &TMH_allowed_payment_targets))
1398 : {
1399 6 : TMH_allowed_payment_targets = GNUNET_strdup ("*");
1400 : }
1401 :
1402 20 : if (GNUNET_OK !=
1403 20 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1404 : "merchant",
1405 : "DEFAULT_PERSONA",
1406 : &TMH_default_persona))
1407 : {
1408 20 : TMH_default_persona = GNUNET_strdup ("expert");
1409 : }
1410 :
1411 20 : if (GNUNET_OK !=
1412 20 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1413 : "merchant",
1414 : "PAYMENT_TARGET_REGEX",
1415 : &TMH_payment_target_regex))
1416 : {
1417 20 : TMH_payment_target_regex = NULL;
1418 : }
1419 : else
1420 : {
1421 0 : if (0 == strlen (TMH_payment_target_regex))
1422 : {
1423 0 : GNUNET_free (TMH_payment_target_regex);
1424 : }
1425 : else
1426 : {
1427 0 : if (0 != regcomp (&TMH_payment_target_re,
1428 : TMH_payment_target_regex,
1429 : REG_NOSUB | REG_EXTENDED))
1430 : {
1431 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1432 : "merchant",
1433 : "PAYMENT_TARGET_REGEX",
1434 : "malformed regular expression");
1435 0 : global_ret = EXIT_NOTCONFIGURED;
1436 0 : GNUNET_free (TMH_payment_target_regex);
1437 0 : GNUNET_SCHEDULER_shutdown ();
1438 0 : return;
1439 : }
1440 : }
1441 : }
1442 20 : if (GNUNET_OK !=
1443 20 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1444 : "merchant",
1445 : "PHONE_REGEX",
1446 : &TMH_phone_regex))
1447 : {
1448 20 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
1449 : "merchant",
1450 : "PHONE_REGEX",
1451 : "no restrictions on phone number specified");
1452 : }
1453 : else
1454 : {
1455 0 : if (0 != regcomp (&TMH_phone_rx,
1456 : TMH_phone_regex,
1457 : REG_EXTENDED))
1458 : {
1459 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1460 : "merchant",
1461 : "PHONE_REGEX",
1462 : "Invalid regex specified");
1463 0 : global_ret = EXIT_NOTCONFIGURED;
1464 0 : GNUNET_SCHEDULER_shutdown ();
1465 0 : return;
1466 : }
1467 : }
1468 :
1469 20 : if (GNUNET_OK !=
1470 20 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1471 : "merchant",
1472 : "HELPER_SMS",
1473 : &TMH_helper_sms))
1474 : {
1475 18 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
1476 : "merchant",
1477 : "HELPER_SMS",
1478 : "no helper specified");
1479 : }
1480 :
1481 20 : if (GNUNET_OK !=
1482 20 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1483 : "merchant",
1484 : "HELPER_EMAIL",
1485 : &TMH_helper_email))
1486 : {
1487 18 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
1488 : "merchant",
1489 : "HELPER_EMAIL",
1490 : "no helper specified");
1491 : }
1492 :
1493 : {
1494 : char *tan_channels;
1495 :
1496 20 : if (GNUNET_OK ==
1497 20 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1498 : "merchant",
1499 : "MANDATORY_TAN_CHANNELS",
1500 : &tan_channels))
1501 : {
1502 1 : for (char *tok = strtok (tan_channels,
1503 : " ");
1504 3 : NULL != tok;
1505 2 : tok = strtok (NULL,
1506 : " "))
1507 : {
1508 2 : if (0 == strcasecmp (tok,
1509 : "sms"))
1510 : {
1511 1 : if (NULL == TMH_helper_sms)
1512 : {
1513 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1514 : "merchant",
1515 : "MANDATORY_TAN_CHANNELS",
1516 : "SMS mandatory, but no HELPER_SMS configured");
1517 0 : global_ret = EXIT_NOTCONFIGURED;
1518 0 : GNUNET_SCHEDULER_shutdown ();
1519 0 : GNUNET_free (tan_channels);
1520 0 : return;
1521 : }
1522 1 : TEH_mandatory_tan_channels |= TMH_TCS_SMS;
1523 : }
1524 1 : else if (0 == strcasecmp (tok,
1525 : "email"))
1526 : {
1527 1 : if (NULL == TMH_helper_email)
1528 : {
1529 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1530 : "merchant",
1531 : "MANDATORY_TAN_CHANNELS",
1532 : "EMAIL mandatory, but no HELPER_EMAIL configured");
1533 0 : global_ret = EXIT_NOTCONFIGURED;
1534 0 : GNUNET_SCHEDULER_shutdown ();
1535 0 : GNUNET_free (tan_channels);
1536 0 : return;
1537 : }
1538 1 : TEH_mandatory_tan_channels |= TMH_TCS_EMAIL;
1539 : }
1540 : else
1541 : {
1542 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1543 : "merchant",
1544 : "MANDATORY_TAN_CHANNELS",
1545 : tok);
1546 0 : global_ret = EXIT_NOTCONFIGURED;
1547 0 : GNUNET_SCHEDULER_shutdown ();
1548 0 : GNUNET_free (tan_channels);
1549 0 : return;
1550 : }
1551 : }
1552 1 : GNUNET_free (tan_channels);
1553 : }
1554 : }
1555 :
1556 20 : if ( (GNUNET_YES == TMH_password_change_mfa) &&
1557 2 : (TMH_TCS_NONE == TEH_mandatory_tan_channels) )
1558 : {
1559 1 : GNUNET_log_config_invalid (
1560 : GNUNET_ERROR_TYPE_ERROR,
1561 : "merchant",
1562 : "PASSWORD_CHANGE_MFA",
1563 : "requires at least one MANDATORY_TAN_CHANNELS entry");
1564 1 : global_ret = EXIT_NOTCONFIGURED;
1565 1 : GNUNET_SCHEDULER_shutdown ();
1566 1 : return;
1567 : }
1568 :
1569 19 : if (GNUNET_OK ==
1570 19 : GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
1571 : "merchant",
1572 : "BASE_URL",
1573 : &TMH_base_url))
1574 : {
1575 18 : if (! TALER_is_web_url (TMH_base_url))
1576 : {
1577 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1578 : "merchant",
1579 : "BASE_URL",
1580 : "Needs to start with 'http://' or 'https://'");
1581 0 : global_ret = EXIT_NOTCONFIGURED;
1582 0 : GNUNET_SCHEDULER_shutdown ();
1583 0 : return;
1584 : }
1585 : }
1586 19 : if (GNUNET_OK ==
1587 19 : GNUNET_CONFIGURATION_get_value_filename (TMH_cfg,
1588 : "merchant",
1589 : "BACKOFFICE_SPA_DIR",
1590 : &TMH_spa_dir))
1591 : {
1592 19 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1593 : "Loading merchant SPA from %s\n",
1594 : TMH_spa_dir);
1595 : }
1596 : else
1597 : {
1598 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1599 : "Loading merchant SPA from default location\n");
1600 : }
1601 :
1602 19 : if (GNUNET_YES ==
1603 19 : GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
1604 : "merchant",
1605 : "FORCE_AUDIT"))
1606 14 : TMH_force_audit = GNUNET_YES;
1607 19 : if (GNUNET_OK !=
1608 19 : TALER_TEMPLATING_init (TALER_MERCHANT_project_data ()))
1609 : {
1610 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1611 : "Failed to setup templates\n");
1612 0 : global_ret = EXIT_NO_RESTART;
1613 0 : GNUNET_SCHEDULER_shutdown ();
1614 0 : return;
1615 : }
1616 19 : if (GNUNET_OK !=
1617 19 : TMH_spa_init (TMH_spa_dir))
1618 : {
1619 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1620 : "Failed to load single page app\n");
1621 0 : global_ret = EXIT_NO_RESTART;
1622 0 : GNUNET_SCHEDULER_shutdown ();
1623 0 : return;
1624 : }
1625 : /* /static/ is currently not used */
1626 : /* (void) TMH_statics_init (); */
1627 19 : if (NULL ==
1628 19 : (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4,
1629 : GNUNET_YES)))
1630 : {
1631 0 : global_ret = EXIT_FAILURE;
1632 0 : GNUNET_SCHEDULER_shutdown ();
1633 0 : return;
1634 : }
1635 19 : if (NULL ==
1636 19 : (TMH_db = TALER_MERCHANTDB_connect (TMH_cfg)))
1637 : {
1638 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1639 : "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
1640 0 : global_ret = EXIT_FAILURE;
1641 0 : GNUNET_SCHEDULER_shutdown ();
1642 0 : return;
1643 : }
1644 19 : elen = TMH_EXCHANGES_init (config);
1645 19 : if (GNUNET_SYSERR == elen)
1646 : {
1647 0 : global_ret = EXIT_NOTCONFIGURED;
1648 0 : GNUNET_SCHEDULER_shutdown ();
1649 0 : return;
1650 : }
1651 19 : if (0 == elen)
1652 : {
1653 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1654 : "Fatal: no trusted exchanges configured. Exiting.\n");
1655 0 : global_ret = EXIT_NOTCONFIGURED;
1656 0 : GNUNET_SCHEDULER_shutdown ();
1657 0 : return;
1658 : }
1659 :
1660 : {
1661 19 : struct GNUNET_DB_EventHeaderP es = {
1662 19 : .size = htons (sizeof (es)),
1663 19 : .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
1664 : };
1665 :
1666 38 : instance_eh = TALER_MERCHANTDB_event_listen (TMH_db,
1667 : &es,
1668 19 : GNUNET_TIME_UNIT_FOREVER_REL,
1669 : &load_instances,
1670 : NULL);
1671 : }
1672 19 : load_instances (NULL,
1673 : NULL,
1674 : 0);
1675 : {
1676 : enum GNUNET_GenericReturnValue ret;
1677 :
1678 19 : ret = TALER_MHD_listen_bind (TMH_cfg,
1679 : "merchant",
1680 : &start_daemon,
1681 : NULL);
1682 19 : switch (ret)
1683 : {
1684 0 : case GNUNET_SYSERR:
1685 0 : global_ret = EXIT_NOTCONFIGURED;
1686 0 : GNUNET_SCHEDULER_shutdown ();
1687 0 : return;
1688 0 : case GNUNET_NO:
1689 0 : if (! have_daemons)
1690 : {
1691 0 : global_ret = EXIT_FAILURE;
1692 0 : GNUNET_SCHEDULER_shutdown ();
1693 0 : return;
1694 : }
1695 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1696 : "Could not open all configured listen sockets\n");
1697 0 : break;
1698 19 : case GNUNET_OK:
1699 19 : break;
1700 : }
1701 : }
1702 19 : global_ret = EXIT_SUCCESS;
1703 : }
1704 :
1705 :
1706 : /**
1707 : * The main function of the serve tool
1708 : *
1709 : * @param argc number of arguments from the command line
1710 : * @param argv command line arguments
1711 : * @return 0 ok, non-zero on error
1712 : */
1713 : int
1714 38 : main (int argc,
1715 : char *const *argv)
1716 : {
1717 : enum GNUNET_GenericReturnValue res;
1718 38 : struct GNUNET_GETOPT_CommandLineOption options[] = {
1719 38 : GNUNET_GETOPT_option_flag ('C',
1720 : "connection-close",
1721 : "force HTTP connections to be closed after each request",
1722 : &merchant_connection_close),
1723 38 : GNUNET_GETOPT_option_flag ('3',
1724 : "http3",
1725 : "enable support for HTTP/2 and HTTP/3",
1726 : &enable_h3),
1727 38 : GNUNET_GETOPT_option_timetravel ('T',
1728 : "timetravel"),
1729 38 : GNUNET_GETOPT_option_version (PACKAGE_VERSION),
1730 : GNUNET_GETOPT_OPTION_END
1731 : };
1732 :
1733 38 : res = GNUNET_PROGRAM_run (
1734 : TALER_MERCHANT_project_data (),
1735 : argc, argv,
1736 : "taler-merchant-httpd",
1737 : "Taler merchant's HTTP backend interface",
1738 : options,
1739 : &run, NULL);
1740 38 : if (GNUNET_SYSERR == res)
1741 0 : return EXIT_NOTCONFIGURED;
1742 38 : if (GNUNET_NO == res)
1743 18 : return EXIT_SUCCESS;
1744 20 : return global_ret;
1745 : }
|