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