Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014-2022 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 : */
24 : #include "platform.h"
25 : #include <taler/taler_dbevents.h>
26 : #include <taler/taler_bank_service.h>
27 : #include <taler/taler_mhd_lib.h>
28 : #include <taler/taler_templating_lib.h>
29 : #include <taler/taler_exchange_service.h>
30 : #include "taler-merchant-httpd_auditors.h"
31 : #include "taler-merchant-httpd_config.h"
32 : #include "taler-merchant-httpd_exchanges.h"
33 : #include "taler-merchant-httpd_get-orders-ID.h"
34 : #include "taler-merchant-httpd_get-tips-ID.h"
35 : #include "taler-merchant-httpd_mhd.h"
36 : #include "taler-merchant-httpd_private-delete-instances-ID.h"
37 : #include "taler-merchant-httpd_private-delete-products-ID.h"
38 : #include "taler-merchant-httpd_private-delete-orders-ID.h"
39 : #include "taler-merchant-httpd_private-delete-reserves-ID.h"
40 : #include "taler-merchant-httpd_private-delete-transfers-ID.h"
41 : #include "taler-merchant-httpd_private-get-instances.h"
42 : #include "taler-merchant-httpd_private-get-instances-ID.h"
43 : #include "taler-merchant-httpd_private-get-instances-ID-kyc.h"
44 : #include "taler-merchant-httpd_private-get-products.h"
45 : #include "taler-merchant-httpd_private-get-products-ID.h"
46 : #include "taler-merchant-httpd_private-get-orders.h"
47 : #include "taler-merchant-httpd_private-get-orders-ID.h"
48 : #include "taler-merchant-httpd_private-get-reserves.h"
49 : #include "taler-merchant-httpd_private-get-reserves-ID.h"
50 : #include "taler-merchant-httpd_private-get-tips-ID.h"
51 : #include "taler-merchant-httpd_private-get-tips.h"
52 : #include "taler-merchant-httpd_private-get-transfers.h"
53 : #include "taler-merchant-httpd_private-patch-instances-ID.h"
54 : #include "taler-merchant-httpd_private-patch-orders-ID-forget.h"
55 : #include "taler-merchant-httpd_private-patch-products-ID.h"
56 : #include "taler-merchant-httpd_private-post-instances.h"
57 : #include "taler-merchant-httpd_private-post-instances-ID-auth.h"
58 : #include "taler-merchant-httpd_private-post-orders.h"
59 : #include "taler-merchant-httpd_private-post-orders-ID-refund.h"
60 : #include "taler-merchant-httpd_private-post-products.h"
61 : #include "taler-merchant-httpd_private-post-products-ID-lock.h"
62 : #include "taler-merchant-httpd_private-post-reserves.h"
63 : #include "taler-merchant-httpd_private-post-reserves-ID-authorize-tip.h"
64 : #include "taler-merchant-httpd_private-post-transfers.h"
65 : #include "taler-merchant-httpd_post-orders-ID-abort.h"
66 : #include "taler-merchant-httpd_post-orders-ID-claim.h"
67 : #include "taler-merchant-httpd_post-orders-ID-paid.h"
68 : #include "taler-merchant-httpd_post-orders-ID-pay.h"
69 : #include "taler-merchant-httpd_post-orders-ID-refund.h"
70 : #include "taler-merchant-httpd_post-tips-ID-pickup.h"
71 : #include "taler-merchant-httpd_reserves.h"
72 : #include "taler-merchant-httpd_spa.h"
73 : #include "taler-merchant-httpd_statics.h"
74 :
75 :
76 : /**
77 : * Fixme: document.
78 : */
79 : #define INSTANCE_STALENESS GNUNET_TIME_UNIT_MINUTES
80 :
81 : /**
82 : * Backlog for listen operation on unix-domain sockets.
83 : */
84 : #define UNIX_BACKLOG 500
85 :
86 : /**
87 : * Default maximum upload size permitted. Can be overridden
88 : * per handler.
89 : */
90 : #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024)
91 :
92 : /**
93 : * Which currency do we use?
94 : */
95 : char *TMH_currency;
96 :
97 : /**
98 : * Inform the auditor for all deposit confirmations (global option)
99 : */
100 : int TMH_force_audit;
101 :
102 : /**
103 : * Connection handle to the our database
104 : */
105 : struct TALER_MERCHANTDB_Plugin *TMH_db;
106 :
107 : /**
108 : * Event handler for instance settings changes.
109 : */
110 : static struct GNUNET_DB_EventHandler *instance_eh;
111 :
112 : /**
113 : * Hashmap pointing at merchant instances by 'id'. An 'id' is
114 : * just a string that identifies a merchant instance. When a frontend
115 : * needs to specify an instance to the backend, it does so by 'id'
116 : */
117 : struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map;
118 :
119 : /**
120 : * How long do we need to keep information on paid contracts on file for tax
121 : * or other legal reasons? Used to block deletions for younger transaction
122 : * data.
123 : */
124 : struct GNUNET_TIME_Relative TMH_legal_expiration;
125 :
126 : /**
127 : * The port we are running on
128 : */
129 : static uint16_t port;
130 :
131 : /**
132 : * Should a "Connection: close" header be added to each HTTP response?
133 : */
134 : static int merchant_connection_close;
135 :
136 : /**
137 : * Global return code
138 : */
139 : static int result;
140 :
141 : /**
142 : * Our configuration.
143 : */
144 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
145 :
146 : /**
147 : * Initial authorization token.
148 : */
149 : char *TMH_default_auth;
150 :
151 :
152 : enum GNUNET_GenericReturnValue
153 0 : TMH_check_auth (const char *token,
154 : struct TALER_MerchantAuthenticationSaltP *salt,
155 : struct TALER_MerchantAuthenticationHashP *hash)
156 : {
157 : struct GNUNET_HashCode val;
158 : char *dec;
159 : size_t dec_len;
160 :
161 0 : if (GNUNET_is_zero (hash))
162 0 : return GNUNET_OK;
163 0 : if (NULL == token)
164 0 : return GNUNET_SYSERR;
165 0 : dec_len = GNUNET_STRINGS_urldecode (token,
166 : strlen (token),
167 : &dec);
168 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
169 : "Checking against token with salt %s\n",
170 : TALER_B2S (salt));
171 0 : GNUNET_assert (GNUNET_YES ==
172 : GNUNET_CRYPTO_kdf (&val,
173 : sizeof (val),
174 : salt,
175 : sizeof (*salt),
176 : dec,
177 : dec_len,
178 : "merchant-instance-auth",
179 : strlen ("merchant-instance-auth"),
180 : NULL,
181 : 0));
182 0 : GNUNET_free (dec);
183 0 : return (0 == GNUNET_memcmp (&val,
184 : &hash->hash))
185 : ? GNUNET_OK
186 0 : : GNUNET_SYSERR;
187 : }
188 :
189 :
190 : void
191 0 : TMH_compute_auth (const char *token,
192 : struct TALER_MerchantAuthenticationSaltP *salt,
193 : struct TALER_MerchantAuthenticationHashP *hash)
194 : {
195 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
196 : salt,
197 : sizeof (*salt));
198 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
199 : "Computing initial auth using token with salt %s\n",
200 : TALER_B2S (salt));
201 0 : GNUNET_assert (GNUNET_YES ==
202 : GNUNET_CRYPTO_kdf (hash,
203 : sizeof (*hash),
204 : salt,
205 : sizeof (*salt),
206 : token,
207 : strlen (token),
208 : "merchant-instance-auth",
209 : strlen ("merchant-instance-auth"),
210 : NULL,
211 : 0));
212 0 : }
213 :
214 :
215 : void
216 0 : TMH_instance_decref (struct TMH_MerchantInstance *mi)
217 : {
218 : struct TMH_WireMethod *wm;
219 :
220 0 : mi->rc--;
221 0 : if (0 != mi->rc)
222 0 : return;
223 0 : TMH_force_get_orders_resume (mi);
224 0 : while (NULL != (wm = (mi->wm_head)))
225 : {
226 0 : GNUNET_CONTAINER_DLL_remove (mi->wm_head,
227 : mi->wm_tail,
228 : wm);
229 0 : GNUNET_free (wm->payto_uri);
230 0 : GNUNET_free (wm->wire_method);
231 0 : GNUNET_free (wm);
232 : }
233 :
234 0 : GNUNET_free (mi->settings.id);
235 0 : GNUNET_free (mi->settings.name);
236 0 : GNUNET_free (mi->settings.email);
237 0 : GNUNET_free (mi->settings.website);
238 0 : GNUNET_free (mi->settings.logo);
239 0 : json_decref (mi->settings.address);
240 0 : json_decref (mi->settings.jurisdiction);
241 0 : GNUNET_free (mi);
242 : }
243 :
244 :
245 : enum GNUNET_GenericReturnValue
246 0 : TMH_instance_free_cb (void *cls,
247 : const struct GNUNET_HashCode *key,
248 : void *value)
249 : {
250 0 : struct TMH_MerchantInstance *mi = value;
251 :
252 : (void) cls;
253 : (void) key;
254 0 : GNUNET_assert (GNUNET_OK ==
255 : GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map,
256 : &mi->h_instance,
257 : mi));
258 0 : TMH_instance_decref (mi);
259 0 : return GNUNET_YES;
260 : }
261 :
262 :
263 : /**
264 : * Shutdown task (magically invoked when the application is being
265 : * quit)
266 : *
267 : * @param cls NULL
268 : */
269 : static void
270 0 : do_shutdown (void *cls)
271 : {
272 : (void) cls;
273 0 : TMH_force_ac_resume ();
274 0 : TMH_force_pc_resume ();
275 0 : TMH_force_kyc_resume ();
276 0 : TMH_force_rc_resume ();
277 0 : TMH_force_gorc_resume ();
278 0 : TMH_force_post_transfers_resume ();
279 0 : TMH_force_tip_pickup_resume ();
280 0 : TMH_force_wallet_get_order_resume ();
281 0 : TMH_force_wallet_refund_order_resume ();
282 : {
283 : struct MHD_Daemon *mhd;
284 :
285 0 : mhd = TALER_MHD_daemon_stop ();
286 0 : if (NULL != mhd)
287 0 : MHD_stop_daemon (mhd);
288 : }
289 0 : TMH_RESERVES_done ();
290 0 : if (NULL != instance_eh)
291 : {
292 0 : TMH_db->event_listen_cancel (instance_eh);
293 0 : instance_eh = NULL;
294 : }
295 0 : if (NULL != TMH_db)
296 : {
297 0 : TALER_MERCHANTDB_plugin_unload (TMH_db);
298 0 : TMH_db = NULL;
299 : }
300 0 : TMH_EXCHANGES_done ();
301 0 : TMH_AUDITORS_done ();
302 0 : if (NULL != TMH_by_id_map)
303 : {
304 0 : GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
305 : &TMH_instance_free_cb,
306 : NULL);
307 0 : GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map);
308 0 : TMH_by_id_map = NULL;
309 : }
310 0 : TALER_TEMPLATING_done ();
311 0 : }
312 :
313 :
314 : /**
315 : * Function called whenever MHD is done with a request. If the
316 : * request was a POST, we may have stored a `struct Buffer *` in the
317 : * @a con_cls that might still need to be cleaned up. Call the
318 : * respective function to free the memory.
319 : *
320 : * @param cls client-defined closure
321 : * @param connection connection handle
322 : * @param con_cls value as set by the last call to
323 : * the #MHD_AccessHandlerCallback
324 : * @param toe reason for request termination
325 : * @see #MHD_OPTION_NOTIFY_COMPLETED
326 : * @ingroup request
327 : */
328 : static void
329 0 : handle_mhd_completion_callback (void *cls,
330 : struct MHD_Connection *connection,
331 : void **con_cls,
332 : enum MHD_RequestTerminationCode toe)
333 : {
334 0 : struct TMH_HandlerContext *hc = *con_cls;
335 :
336 : (void) cls;
337 0 : if (NULL == hc)
338 0 : return;
339 0 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
340 : {
341 : #if MHD_VERSION >= 0x00097304
342 : const union MHD_ConnectionInfo *ci;
343 0 : unsigned int http_status = 0;
344 :
345 0 : ci = MHD_get_connection_info (connection,
346 : MHD_CONNECTION_INFO_HTTP_STATUS);
347 0 : if (NULL != ci)
348 0 : http_status = ci->http_status;
349 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
350 : "Request for `%s' completed with HTTP status %u (%d)\n",
351 : hc->url,
352 : http_status,
353 : toe);
354 : #else
355 : (void) connection;
356 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
357 : "Finished handling request for `%s' with MHD termination code %d\n",
358 : hc->url,
359 : (int) toe);
360 : #endif
361 : }
362 0 : if (NULL != hc->cc)
363 0 : hc->cc (hc->ctx);
364 0 : TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
365 0 : GNUNET_free (hc->infix);
366 0 : if (NULL != hc->request_body)
367 0 : json_decref (hc->request_body);
368 0 : if (NULL != hc->instance)
369 0 : TMH_instance_decref (hc->instance);
370 0 : GNUNET_free (hc);
371 0 : *con_cls = NULL;
372 : }
373 :
374 :
375 : struct TMH_MerchantInstance *
376 0 : TMH_lookup_instance (const char *instance_id)
377 : {
378 : struct GNUNET_HashCode h_instance;
379 :
380 0 : if (NULL == instance_id)
381 0 : instance_id = "default";
382 0 : GNUNET_CRYPTO_hash (instance_id,
383 : strlen (instance_id),
384 : &h_instance);
385 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386 : "Looking for by-id key %s of '%s' in hashmap\n",
387 : GNUNET_h2s (&h_instance),
388 : instance_id);
389 : /* We're fine if that returns NULL, the calling routine knows how
390 : to handle that */
391 0 : return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map,
392 : &h_instance);
393 : }
394 :
395 :
396 : /**
397 : * Add instance definition to our active set of instances.
398 : *
399 : * @param[in,out] mi merchant instance details to define
400 : * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already
401 : */
402 : enum GNUNET_GenericReturnValue
403 0 : TMH_add_instance (struct TMH_MerchantInstance *mi)
404 : {
405 : const char *id;
406 : int ret;
407 :
408 0 : id = mi->settings.id;
409 0 : if (NULL == id)
410 0 : id = "default";
411 0 : GNUNET_CRYPTO_hash (id,
412 : strlen (id),
413 : &mi->h_instance);
414 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
415 : "Looking for by-id key %s of `%s' in hashmap\n",
416 : GNUNET_h2s (&mi->h_instance),
417 : id);
418 0 : ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map,
419 0 : &mi->h_instance,
420 : mi,
421 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
422 0 : if (GNUNET_OK == ret)
423 : {
424 0 : GNUNET_assert (mi->rc < UINT_MAX);
425 0 : mi->rc++;
426 : }
427 0 : return ret;
428 : }
429 :
430 :
431 : /**
432 : * Handle a OPTIONS "*" request.
433 : *
434 : * @param rh context of the handler
435 : * @param connection the MHD connection to handle
436 : * @param[in,out] hc context with further information about the request
437 : * @return MHD result code
438 : */
439 : static MHD_RESULT
440 0 : handle_server_options (const struct TMH_RequestHandler *rh,
441 : struct MHD_Connection *connection,
442 : struct TMH_HandlerContext *hc)
443 : {
444 : (void) rh;
445 : (void) hc;
446 0 : return TALER_MHD_reply_cors_preflight (connection);
447 : }
448 :
449 :
450 : /**
451 : * Extract the token from authorization header value @a auth.
452 : *
453 : * @param auth pointer to authorization header value,
454 : * will be updated to point to the start of the token
455 : * or set to NULL if header value is invalid
456 : */
457 : static void
458 0 : extract_token (const char **auth)
459 : {
460 0 : const char *bearer = "Bearer ";
461 0 : const char *tok = *auth;
462 :
463 0 : if (0 != strncmp (tok, bearer, strlen (bearer)))
464 : {
465 0 : *auth = NULL;
466 0 : return;
467 : }
468 0 : tok = tok + strlen (bearer);
469 0 : while (' ' == *tok)
470 0 : tok++;
471 0 : if (0 != strncasecmp (tok,
472 : RFC_8959_PREFIX,
473 : strlen (RFC_8959_PREFIX)))
474 : {
475 0 : *auth = NULL;
476 0 : return;
477 : }
478 0 : *auth = tok;
479 : }
480 :
481 :
482 : /**
483 : * Checks if the @a rh matches the given (parsed) URL.
484 : *
485 : * @param rh handler to compare against
486 : * @param url the main URL (without "/private/" prefix, if any)
487 : * @param prefix_strlen length of the prefix, i.e. 8 for '/orders/' or 7 for '/config'
488 : * @param infix_url infix text, i.e. "$ORDER_ID".
489 : * @param infix_strlen length of the string in @a infix_url
490 : * @param suffix_url suffix, i.e. "/refund", including the "/"
491 : * @param suffix_strlen number of characters in @a suffix_url
492 : * @return true if @a rh matches this request
493 : */
494 : static bool
495 0 : prefix_match (const struct TMH_RequestHandler *rh,
496 : const char *url,
497 : size_t prefix_strlen,
498 : const char *infix_url,
499 : size_t infix_strlen,
500 : const char *suffix_url,
501 : size_t suffix_strlen)
502 : {
503 0 : if ( (prefix_strlen != strlen (rh->url_prefix)) ||
504 0 : (0 != memcmp (url,
505 0 : rh->url_prefix,
506 : prefix_strlen)) )
507 0 : return false;
508 0 : if (! rh->have_id_segment)
509 : {
510 : /* Require /$PREFIX/$SUFFIX or /$PREFIX */
511 0 : if (NULL != suffix_url)
512 0 : return false; /* too many segments to match */
513 0 : if ( (NULL == infix_url) /* either or */
514 0 : ^ (NULL == rh->url_suffix) )
515 0 : return false; /* suffix existence mismatch */
516 : /* If /$PREFIX/$SUFFIX, check $SUFFIX matches */
517 0 : if ( (NULL != infix_url) &&
518 0 : ( (infix_strlen != strlen (rh->url_suffix)) ||
519 0 : (0 != memcmp (infix_url,
520 0 : rh->url_suffix,
521 : infix_strlen)) ) )
522 0 : return false; /* cannot use infix as suffix: content mismatch */
523 : }
524 : else
525 : {
526 : /* Require /$PREFIX/$ID or /$PREFIX/$ID/$SUFFIX */
527 0 : if (NULL == infix_url)
528 0 : return false; /* infix existence mismatch */
529 0 : if ( ( (NULL == suffix_url)
530 0 : ^ (NULL == rh->url_suffix) ) )
531 0 : return false; /* suffix existence mismatch */
532 0 : if ( (NULL != suffix_url) &&
533 0 : ( (suffix_strlen != strlen (rh->url_suffix)) ||
534 0 : (0 != memcmp (suffix_url,
535 0 : rh->url_suffix,
536 : suffix_strlen)) ) )
537 0 : return false; /* suffix content mismatch */
538 : }
539 0 : return true;
540 : }
541 :
542 :
543 : /**
544 : * A client has requested the given url using the given method
545 : * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
546 : * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
547 : * must call MHD callbacks to provide content to give back to the
548 : * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
549 : * #MHD_HTTP_NOT_FOUND, etc.).
550 : *
551 : * @param cls argument given together with the function
552 : * pointer when the handler was registered with MHD
553 : * @param connection the MHD connection to handle
554 : * @param url the requested url
555 : * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
556 : * #MHD_HTTP_METHOD_PUT, etc.)
557 : * @param version the HTTP version string (i.e.
558 : * #MHD_HTTP_VERSION_1_1)
559 : * @param upload_data the data being uploaded (excluding HEADERS,
560 : * for a POST that fits into memory and that is encoded
561 : * with a supported encoding, the POST data will NOT be
562 : * given in upload_data and is instead available as
563 : * part of #MHD_get_connection_values; very large POST
564 : * data *will* be made available incrementally in
565 : * @a upload_data)
566 : * @param upload_data_size set initially to the size of the
567 : * @a upload_data provided; the method must update this
568 : * value to the number of bytes NOT processed;
569 : * @param con_cls pointer that the callback can set to some
570 : * address and that will be preserved by MHD for future
571 : * calls for this request; since the access handler may
572 : * be called many times (i.e., for a PUT/POST operation
573 : * with plenty of upload data) this allows the application
574 : * to easily associate some request-specific state.
575 : * If necessary, this state can be cleaned up in the
576 : * global #MHD_RequestCompletedCallback (which
577 : * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
578 : * Initially, `*con_cls` will be NULL.
579 : * @return #MHD_YES if the connection was handled successfully,
580 : * #MHD_NO if the socket must be closed due to a serious
581 : * error while handling the request
582 : */
583 : static MHD_RESULT
584 0 : url_handler (void *cls,
585 : struct MHD_Connection *connection,
586 : const char *url,
587 : const char *method,
588 : const char *version,
589 : const char *upload_data,
590 : size_t *upload_data_size,
591 : void **con_cls)
592 : {
593 : static struct TMH_RequestHandler management_handlers[] = {
594 : /* GET /instances */
595 : {
596 : .url_prefix = "/instances",
597 : .method = MHD_HTTP_METHOD_GET,
598 : .skip_instance = true,
599 : .default_only = true,
600 : .handler = &TMH_private_get_instances
601 : },
602 : /* POST /instances */
603 : {
604 : .url_prefix = "/instances",
605 : .method = MHD_HTTP_METHOD_POST,
606 : .skip_instance = true,
607 : .default_only = true,
608 : .handler = &TMH_private_post_instances,
609 : /* allow instance data of up to 8 MB, that should be plenty;
610 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
611 : would require further changes to the allocation logic
612 : in the code... */
613 : .max_upload = 1024 * 1024 * 8
614 : },
615 : /* GET /instances/$ID/ */
616 : {
617 : .url_prefix = "/instances/",
618 : .method = MHD_HTTP_METHOD_GET,
619 : .skip_instance = true,
620 : .default_only = true,
621 : .have_id_segment = true,
622 : .handler = &TMH_private_get_instances_default_ID
623 : },
624 : /* DELETE /instances/$ID */
625 : {
626 : .url_prefix = "/instances/",
627 : .method = MHD_HTTP_METHOD_DELETE,
628 : .skip_instance = true,
629 : .default_only = true,
630 : .have_id_segment = true,
631 : .handler = &TMH_private_delete_instances_default_ID
632 : },
633 : /* PATCH /instances/$ID */
634 : {
635 : .url_prefix = "/instances/",
636 : .method = MHD_HTTP_METHOD_PATCH,
637 : .skip_instance = true,
638 : .default_only = true,
639 : .have_id_segment = true,
640 : .handler = &TMH_private_patch_instances_default_ID,
641 : /* allow instance data of up to 8 MB, that should be plenty;
642 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
643 : would require further changes to the allocation logic
644 : in the code... */
645 : .max_upload = 1024 * 1024 * 8
646 : },
647 : /* POST /auth: */
648 : {
649 : .url_prefix = "/instances/",
650 : .url_suffix = "auth",
651 : .method = MHD_HTTP_METHOD_POST,
652 : .skip_instance = true,
653 : .default_only = true,
654 : .have_id_segment = true,
655 : .handler = &TMH_private_post_instances_default_ID_auth,
656 : /* Body should be pretty small. */
657 : .max_upload = 1024 * 1024
658 : },
659 : /* POST /kyc: */
660 : {
661 : .url_prefix = "/instances/",
662 : .url_suffix = "kyc",
663 : .method = MHD_HTTP_METHOD_GET,
664 : .skip_instance = true,
665 : .default_only = true,
666 : .have_id_segment = true,
667 : .handler = &TMH_private_get_instances_default_ID_kyc,
668 : },
669 : {
670 : .url_prefix = NULL
671 : }
672 : };
673 :
674 : static struct TMH_RequestHandler private_handlers[] = {
675 : /* GET /instances/$ID/: */
676 : {
677 : .url_prefix = "/",
678 : .method = MHD_HTTP_METHOD_GET,
679 : .handler = &TMH_private_get_instances_ID
680 : },
681 : /* DELETE /instances/$ID/: */
682 : {
683 : .url_prefix = "/",
684 : .method = MHD_HTTP_METHOD_DELETE,
685 : .allow_deleted_instance = true,
686 : .handler = &TMH_private_delete_instances_ID
687 : },
688 : /* PATCH /instances/$ID/: */
689 : {
690 : .url_prefix = "/",
691 : .method = MHD_HTTP_METHOD_PATCH,
692 : .handler = &TMH_private_patch_instances_ID,
693 : .allow_deleted_instance = true,
694 : /* allow instance data of up to 8 MB, that should be plenty;
695 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
696 : would require further changes to the allocation logic
697 : in the code... */
698 : .max_upload = 1024 * 1024 * 8
699 : },
700 : /* POST /auth: */
701 : {
702 : .url_prefix = "/auth",
703 : .method = MHD_HTTP_METHOD_POST,
704 : .handler = &TMH_private_post_instances_ID_auth,
705 : /* Body should be pretty small. */
706 : .max_upload = 1024 * 1024,
707 : },
708 : /* GET /kyc: */
709 : {
710 : .url_prefix = "/kyc",
711 : .method = MHD_HTTP_METHOD_GET,
712 : .handler = &TMH_private_get_instances_ID_kyc,
713 : },
714 : /* GET /products: */
715 : {
716 : .url_prefix = "/products",
717 : .method = MHD_HTTP_METHOD_GET,
718 : .handler = &TMH_private_get_products
719 : },
720 : /* POST /products: */
721 : {
722 : .url_prefix = "/products",
723 : .method = MHD_HTTP_METHOD_POST,
724 : .handler = &TMH_private_post_products,
725 : /* allow product data of up to 8 MB, that should be plenty;
726 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
727 : would require further changes to the allocation logic
728 : in the code... */
729 : .max_upload = 1024 * 1024 * 8
730 : },
731 : /* GET /products/$ID/: */
732 : {
733 : .url_prefix = "/products/",
734 : .method = MHD_HTTP_METHOD_GET,
735 : .have_id_segment = true,
736 : .allow_deleted_instance = true,
737 : .handler = &TMH_private_get_products_ID
738 : },
739 : /* DELETE /products/$ID/: */
740 : {
741 : .url_prefix = "/products/",
742 : .method = MHD_HTTP_METHOD_DELETE,
743 : .have_id_segment = true,
744 : .allow_deleted_instance = true,
745 : .handler = &TMH_private_delete_products_ID
746 : },
747 : /* PATCH /products/$ID/: */
748 : {
749 : .url_prefix = "/products/",
750 : .method = MHD_HTTP_METHOD_PATCH,
751 : .have_id_segment = true,
752 : .allow_deleted_instance = true,
753 : .handler = &TMH_private_patch_products_ID,
754 : /* allow product data of up to 8 MB, that should be plenty;
755 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
756 : would require further changes to the allocation logic
757 : in the code... */
758 : .max_upload = 1024 * 1024 * 8
759 : },
760 : /* POST /products/$ID/lock: */
761 : {
762 : .url_prefix = "/products/",
763 : .url_suffix = "lock",
764 : .method = MHD_HTTP_METHOD_POST,
765 : .have_id_segment = true,
766 : .handler = &TMH_private_post_products_ID_lock,
767 : /* the body should be pretty small, allow 1 MB of upload
768 : to set a conservative bound for sane wallets */
769 : .max_upload = 1024 * 1024
770 : },
771 : /* POST /orders: */
772 : {
773 : .url_prefix = "/orders",
774 : .method = MHD_HTTP_METHOD_POST,
775 : .handler = &TMH_private_post_orders,
776 : /* allow contracts of up to 8 MB, that should be plenty;
777 : note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
778 : would require further changes to the allocation logic
779 : in the code... */
780 : .max_upload = 1024 * 1024 * 8
781 : },
782 : /* GET /orders/$ID: */
783 : {
784 : .url_prefix = "/orders/",
785 : .method = MHD_HTTP_METHOD_GET,
786 : .have_id_segment = true,
787 : .allow_deleted_instance = true,
788 : .handler = &TMH_private_get_orders_ID
789 : },
790 : /* GET /orders: */
791 : {
792 : .url_prefix = "/orders",
793 : .method = MHD_HTTP_METHOD_GET,
794 : .allow_deleted_instance = true,
795 : .handler = &TMH_private_get_orders
796 : },
797 : /* POST /orders/$ID/refund: */
798 : {
799 : .url_prefix = "/orders/",
800 : .url_suffix = "refund",
801 : .method = MHD_HTTP_METHOD_POST,
802 : .have_id_segment = true,
803 : .handler = &TMH_private_post_orders_ID_refund,
804 : /* the body should be pretty small, allow 1 MB of upload
805 : to set a conservative bound for sane wallets */
806 : .max_upload = 1024 * 1024
807 : },
808 : /* PATCH /orders/$ID/forget: */
809 : {
810 : .url_prefix = "/orders/",
811 : .url_suffix = "forget",
812 : .method = MHD_HTTP_METHOD_PATCH,
813 : .have_id_segment = true,
814 : .allow_deleted_instance = true,
815 : .handler = &TMH_private_patch_orders_ID_forget,
816 : /* the body should be pretty small, allow 1 MB of upload
817 : to set a conservative bound for sane wallets */
818 : .max_upload = 1024 * 1024
819 : },
820 : /* DELETE /orders/$ID: */
821 : {
822 : .url_prefix = "/orders/",
823 : .method = MHD_HTTP_METHOD_DELETE,
824 : .have_id_segment = true,
825 : .allow_deleted_instance = true,
826 : .handler = &TMH_private_delete_orders_ID
827 : },
828 : /* POST /reserves: */
829 : {
830 : .url_prefix = "/reserves",
831 : .method = MHD_HTTP_METHOD_POST,
832 : .handler = &TMH_private_post_reserves,
833 : /* the body should be pretty small, allow 1 MB of upload
834 : to set a conservative bound for sane wallets */
835 : .max_upload = 1024 * 1024
836 : },
837 : /* DELETE /reserves/$ID: */
838 : {
839 : .url_prefix = "/reserves/",
840 : .have_id_segment = true,
841 : .allow_deleted_instance = true,
842 : .method = MHD_HTTP_METHOD_DELETE,
843 : .handler = &TMH_private_delete_reserves_ID
844 : },
845 : /* POST /reserves/$ID/authorize-tip: */
846 : {
847 : .url_prefix = "/reserves/",
848 : .url_suffix = "authorize-tip",
849 : .have_id_segment = true,
850 : .method = MHD_HTTP_METHOD_POST,
851 : .handler = &TMH_private_post_reserves_ID_authorize_tip,
852 : /* the body should be pretty small, allow 1 MB of upload
853 : to set a conservative bound for sane wallets */
854 : .max_upload = 1024 * 1024
855 : },
856 : /* POST /tips: */
857 : {
858 : .url_prefix = "/tips",
859 : .method = MHD_HTTP_METHOD_POST,
860 : .handler = &TMH_private_post_tips,
861 : /* the body should be pretty small, allow 1 MB of upload
862 : to set a conservative bound for sane wallets */
863 : .max_upload = 1024 * 1024
864 : },
865 : /* GET /tips: */
866 : {
867 : .url_prefix = "/tips",
868 : .allow_deleted_instance = true,
869 : .method = MHD_HTTP_METHOD_GET,
870 : .handler = &TMH_private_get_tips
871 : },
872 : /* GET /tips/$ID: */
873 : {
874 : .url_prefix = "/tips/",
875 : .method = MHD_HTTP_METHOD_GET,
876 : .allow_deleted_instance = true,
877 : .have_id_segment = true,
878 : .handler = &TMH_private_get_tips_ID
879 : },
880 : /* GET /reserves: */
881 : {
882 : .url_prefix = "/reserves",
883 : .allow_deleted_instance = true,
884 : .method = MHD_HTTP_METHOD_GET,
885 : .handler = &TMH_private_get_reserves
886 : },
887 : /* GET /reserves/$ID: */
888 : {
889 : .url_prefix = "/reserves/",
890 : .allow_deleted_instance = true,
891 : .have_id_segment = true,
892 : .method = MHD_HTTP_METHOD_GET,
893 : .handler = &TMH_private_get_reserves_ID
894 : },
895 : /* POST /transfers: */
896 : {
897 : .url_prefix = "/transfers",
898 : .method = MHD_HTTP_METHOD_POST,
899 : .allow_deleted_instance = true,
900 : .handler = &TMH_private_post_transfers,
901 : /* the body should be pretty small, allow 1 MB of upload
902 : to set a conservative bound for sane wallets */
903 : .max_upload = 1024 * 1024
904 : },
905 : /* DELETE /transfers/$ID: */
906 : {
907 : .url_prefix = "/transfers/",
908 : .method = MHD_HTTP_METHOD_DELETE,
909 : .allow_deleted_instance = true,
910 : .handler = &TMH_private_delete_transfers_ID,
911 : .have_id_segment = true,
912 : /* the body should be pretty small, allow 1 MB of upload
913 : to set a conservative bound for sane wallets */
914 : .max_upload = 1024 * 1024
915 : },
916 : /* GET /transfers: */
917 : {
918 : .url_prefix = "/transfers",
919 : .method = MHD_HTTP_METHOD_GET,
920 : .allow_deleted_instance = true,
921 : .handler = &TMH_private_get_transfers
922 : },
923 : {
924 : .url_prefix = NULL
925 : }
926 : };
927 : static struct TMH_RequestHandler public_handlers[] = {
928 : {
929 : .url_prefix = "/",
930 : .method = MHD_HTTP_METHOD_GET,
931 : .mime_type = "text/html",
932 : .skip_instance = true,
933 : .handler = &TMH_return_spa,
934 : .response_code = MHD_HTTP_OK
935 : },
936 : {
937 : .url_prefix = "/agpl",
938 : .method = MHD_HTTP_METHOD_GET,
939 : .skip_instance = true,
940 : .handler = &TMH_MHD_handler_agpl_redirect
941 : },
942 : {
943 : .url_prefix = "/config",
944 : .method = MHD_HTTP_METHOD_GET,
945 : .skip_instance = true,
946 : .default_only = true,
947 : .handler = &MH_handler_config
948 : },
949 : /* Also serve the same /config per instance */
950 : {
951 : .url_prefix = "/config",
952 : .method = MHD_HTTP_METHOD_GET,
953 : .skip_instance = false,
954 : .allow_deleted_instance = true,
955 : .handler = &MH_handler_config
956 : },
957 : /* POST /orders/$ID/abort: */
958 : {
959 : .url_prefix = "/orders/",
960 : .have_id_segment = true,
961 : .url_suffix = "abort",
962 : .method = MHD_HTTP_METHOD_POST,
963 : .handler = &TMH_post_orders_ID_abort,
964 : /* wallet may give us many coins to sign, allow 1 MB of upload
965 : to set a conservative bound for sane wallets */
966 : .max_upload = 1024 * 1024
967 : },
968 : /* POST /orders/$ID/claim: */
969 : {
970 : .url_prefix = "/orders/",
971 : .have_id_segment = true,
972 : .url_suffix = "claim",
973 : .method = MHD_HTTP_METHOD_POST,
974 : .handler = &TMH_post_orders_ID_claim,
975 : /* the body should be pretty small, allow 1 MB of upload
976 : to set a conservative bound for sane wallets */
977 : .max_upload = 1024 * 1024
978 : },
979 : /* POST /orders/$ID/pay: */
980 : {
981 : .url_prefix = "/orders/",
982 : .have_id_segment = true,
983 : .url_suffix = "pay",
984 : .method = MHD_HTTP_METHOD_POST,
985 : .handler = &TMH_post_orders_ID_pay,
986 : /* wallet may give us many coins to sign, allow 1 MB of upload
987 : to set a conservative bound for sane wallets */
988 : .max_upload = 1024 * 1024
989 : },
990 : /* POST /orders/$ID/paid: */
991 : {
992 : .url_prefix = "/orders/",
993 : .have_id_segment = true,
994 : .allow_deleted_instance = true,
995 : .url_suffix = "paid",
996 : .method = MHD_HTTP_METHOD_POST,
997 : .handler = &TMH_post_orders_ID_paid,
998 : /* the body should be pretty small, allow 1 MB of upload
999 : to set a conservative bound for sane wallets */
1000 : .max_upload = 1024 * 1024
1001 : },
1002 : /* POST /orders/$ID/refund: */
1003 : {
1004 : .url_prefix = "/orders/",
1005 : .have_id_segment = true,
1006 : .allow_deleted_instance = true,
1007 : .url_suffix = "refund",
1008 : .method = MHD_HTTP_METHOD_POST,
1009 : .handler = &TMH_post_orders_ID_refund,
1010 : /* the body should be pretty small, allow 1 MB of upload
1011 : to set a conservative bound for sane wallets */
1012 : .max_upload = 1024 * 1024
1013 : },
1014 : /* GET /orders/$ID: */
1015 : {
1016 : .url_prefix = "/orders/",
1017 : .method = MHD_HTTP_METHOD_GET,
1018 : .allow_deleted_instance = true,
1019 : .have_id_segment = true,
1020 : .handler = &TMH_get_orders_ID
1021 : },
1022 : /* GET /tips/$ID: */
1023 : {
1024 : .url_prefix = "/tips/",
1025 : .method = MHD_HTTP_METHOD_GET,
1026 : .allow_deleted_instance = true,
1027 : .have_id_segment = true,
1028 : .handler = &TMH_get_tips_ID
1029 : },
1030 : /* POST /tips/$ID/pickup: */
1031 : {
1032 : .url_prefix = "/tips/",
1033 : .method = MHD_HTTP_METHOD_POST,
1034 : .have_id_segment = true,
1035 : .allow_deleted_instance = true,
1036 : .url_suffix = "pickup",
1037 : .handler = &TMH_post_tips_ID_pickup,
1038 : /* wallet may give us many coins to sign, allow 1 MB of upload
1039 : to set a conservative bound for sane wallets */
1040 : .max_upload = 1024 * 1024
1041 : },
1042 : /* GET /static/ *: */
1043 : {
1044 : .url_prefix = "/static/",
1045 : .method = MHD_HTTP_METHOD_GET,
1046 : .have_id_segment = true,
1047 : .handler = &TMH_return_static
1048 : },
1049 : {
1050 : .url_prefix = "*",
1051 : .method = MHD_HTTP_METHOD_OPTIONS,
1052 : .handler = &handle_server_options
1053 : },
1054 : {
1055 : .url_prefix = NULL
1056 : }
1057 : };
1058 0 : struct TMH_HandlerContext *hc = *con_cls;
1059 : struct TMH_RequestHandler *handlers;
1060 0 : bool use_default = false;
1061 :
1062 : (void) cls;
1063 : (void) version;
1064 0 : if (NULL != hc)
1065 : {
1066 : /* MHD calls us again for a request, for first call
1067 : see 'else' case below */
1068 0 : GNUNET_assert (NULL != hc->rh);
1069 0 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
1070 0 : if ( (hc->has_body) &&
1071 0 : (NULL == hc->request_body) )
1072 : {
1073 0 : size_t mul = hc->rh->max_upload;
1074 : enum GNUNET_GenericReturnValue res;
1075 :
1076 0 : if (0 == mul)
1077 0 : mul = DEFAULT_MAX_UPLOAD_SIZE;
1078 0 : if ( (hc->total_upload + *upload_data_size < hc->total_upload) ||
1079 0 : (hc->total_upload + *upload_data_size > mul) )
1080 : {
1081 : /* Client exceeds upload limit. Should _usually_ be checked earlier
1082 : when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with
1083 : chunked encoding an uploader MAY have omitted this, and thus
1084 : not permitted us to check on time. In this case, we just close
1085 : the connection once it exceeds our limit (instead of waiting
1086 : for the upload to complete and then fail). This could theoretically
1087 : cause some clients to retry, alas broken or malicious clients
1088 : are likely to retry anyway, so little we can do about it, and
1089 : failing earlier seems the best option here. */
1090 0 : GNUNET_break_op (0);
1091 0 : return MHD_NO;
1092 : }
1093 0 : hc->total_upload += *upload_data_size;
1094 0 : res = TALER_MHD_parse_post_json (connection,
1095 : &hc->json_parse_context,
1096 : upload_data,
1097 : upload_data_size,
1098 : &hc->request_body);
1099 0 : if (GNUNET_SYSERR == res)
1100 0 : return MHD_NO;
1101 : /* A error response was already generated */
1102 0 : if ( (GNUNET_NO == res) ||
1103 : /* or, need more data to accomplish parsing */
1104 0 : (NULL == hc->request_body) )
1105 0 : return MHD_YES; /* let MHD call us *again* */
1106 : }
1107 : /* Upload complete (if any), call handler to generate reply */
1108 0 : return hc->rh->handler (hc->rh,
1109 : connection,
1110 : hc);
1111 : }
1112 0 : hc = GNUNET_new (struct TMH_HandlerContext);
1113 0 : *con_cls = hc;
1114 0 : GNUNET_async_scope_fresh (&hc->async_scope_id);
1115 0 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
1116 0 : hc->url = url;
1117 : {
1118 : const char *correlation_id;
1119 :
1120 0 : correlation_id = MHD_lookup_connection_value (connection,
1121 : MHD_HEADER_KIND,
1122 : "Taler-Correlation-Id");
1123 0 : if ( (NULL != correlation_id) &&
1124 : (GNUNET_YES !=
1125 0 : GNUNET_CURL_is_valid_scope_id (correlation_id)) )
1126 : {
1127 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1128 : "Illegal incoming correlation ID\n");
1129 0 : correlation_id = NULL;
1130 : }
1131 0 : if (NULL != correlation_id)
1132 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1133 : "Handling request for (%s) URL '%s', correlation_id=%s\n",
1134 : method,
1135 : url,
1136 : correlation_id);
1137 : else
1138 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1139 : "Handling request (%s) for URL '%s'\n",
1140 : method,
1141 : url);
1142 : }
1143 :
1144 0 : if (0 == strcasecmp (method,
1145 : MHD_HTTP_METHOD_HEAD))
1146 0 : method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
1147 :
1148 :
1149 : /* Find out the merchant backend instance for the request.
1150 : * If there is an instance, remove the instance specification
1151 : * from the beginning of the request URL. */
1152 : {
1153 0 : const char *instance_prefix = "/instances/";
1154 :
1155 0 : if (0 == strncmp (url,
1156 : instance_prefix,
1157 : strlen (instance_prefix)))
1158 : {
1159 : /* url starts with "/instances/" */
1160 0 : const char *istart = url + strlen (instance_prefix);
1161 0 : const char *slash = strchr (istart, '/');
1162 : char *instance_id;
1163 :
1164 0 : if (NULL == slash)
1165 0 : instance_id = GNUNET_strdup (istart);
1166 : else
1167 0 : instance_id = GNUNET_strndup (istart,
1168 : slash - istart);
1169 0 : hc->instance = TMH_lookup_instance (instance_id);
1170 0 : if ( (NULL == hc->instance) &&
1171 0 : (0 == strcmp ("default",
1172 : instance_id)) )
1173 0 : hc->instance = TMH_lookup_instance (NULL);
1174 0 : if ( (0 == strcmp ("default",
1175 0 : instance_id)) &&
1176 0 : (NULL != TMH_default_auth) &&
1177 0 : (NULL != hc->instance) )
1178 : {
1179 : /* Override default instance access control */
1180 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1181 : "Command-line override of access control\n");
1182 0 : TMH_compute_auth (TMH_default_auth,
1183 0 : &hc->instance->auth.auth_salt,
1184 0 : &hc->instance->auth.auth_hash);
1185 0 : hc->instance->auth_override = true;
1186 0 : GNUNET_free (TMH_default_auth);
1187 : }
1188 0 : GNUNET_free (instance_id);
1189 0 : if (NULL == slash)
1190 0 : url = "";
1191 : else
1192 0 : url = slash;
1193 : }
1194 : else
1195 : {
1196 : /* use 'default' */
1197 0 : use_default = true;
1198 0 : hc->instance = TMH_lookup_instance (NULL);
1199 0 : if ( (NULL != TMH_default_auth) &&
1200 0 : (NULL != hc->instance) )
1201 : {
1202 : /* Override default instance access control */
1203 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1204 : "Command-line override of access control\n");
1205 0 : TMH_compute_auth (TMH_default_auth,
1206 0 : &hc->instance->auth.auth_salt,
1207 0 : &hc->instance->auth.auth_hash);
1208 0 : hc->instance->auth_override = true;
1209 0 : GNUNET_free (TMH_default_auth);
1210 : }
1211 : }
1212 0 : if (NULL != hc->instance)
1213 : {
1214 0 : GNUNET_assert (hc->instance->rc < UINT_MAX);
1215 0 : hc->instance->rc++;
1216 : }
1217 : }
1218 :
1219 : {
1220 0 : const char *management_prefix = "/management/";
1221 0 : const char *private_prefix = "/private/";
1222 :
1223 0 : if ( (0 == strncmp (url,
1224 : management_prefix,
1225 : strlen (management_prefix))) )
1226 : {
1227 0 : handlers = management_handlers;
1228 0 : url += strlen (management_prefix) - 1;
1229 : }
1230 0 : else if ( (0 == strncmp (url,
1231 : private_prefix,
1232 0 : strlen (private_prefix))) ||
1233 0 : (0 == strcmp (url,
1234 : "/private")) )
1235 : {
1236 0 : handlers = private_handlers;
1237 0 : url += strlen (private_prefix) - 1;
1238 : }
1239 : else
1240 : {
1241 0 : handlers = public_handlers;
1242 : }
1243 : }
1244 :
1245 0 : if (0 == strcmp (url,
1246 : ""))
1247 0 : url = "/"; /* code below does not like empty string */
1248 :
1249 : {
1250 : /* Matching URL found, but maybe method doesn't match */
1251 : size_t prefix_strlen; /* i.e. 8 for "/orders/", or 7 for "/config" */
1252 0 : const char *infix_url = NULL; /* i.e. "$ORDER_ID", no '/'-es */
1253 0 : size_t infix_strlen = 0; /* number of characters in infix_url */
1254 0 : const char *suffix_url = NULL; /* i.e. "refund", excludes '/' at the beginning */
1255 0 : size_t suffix_strlen = 0; /* number of characters in suffix_url */
1256 :
1257 : /* parse the URL into the three different components */
1258 : {
1259 : const char *slash;
1260 :
1261 0 : slash = strchr (&url[1], '/');
1262 0 : if (NULL == slash)
1263 : {
1264 : /* the prefix was everything */
1265 0 : prefix_strlen = strlen (url);
1266 : }
1267 : else
1268 : {
1269 0 : prefix_strlen = slash - url + 1; /* includes both '/'-es if present! */
1270 0 : infix_url = slash + 1;
1271 0 : slash = strchr (&infix_url[1], '/');
1272 0 : if (NULL == slash)
1273 : {
1274 : /* the infix was the rest */
1275 0 : infix_strlen = strlen (infix_url);
1276 : }
1277 : else
1278 : {
1279 0 : infix_strlen = slash - infix_url; /* excludes both '/'-es */
1280 0 : suffix_url = slash + 1; /* skip the '/' */
1281 0 : suffix_strlen = strlen (suffix_url);
1282 : }
1283 0 : hc->infix = GNUNET_strndup (infix_url,
1284 : infix_strlen);
1285 : }
1286 : }
1287 :
1288 : /* find matching handler */
1289 : {
1290 0 : bool url_found = false;
1291 :
1292 0 : for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
1293 : {
1294 0 : struct TMH_RequestHandler *rh = &handlers[i];
1295 :
1296 0 : if (rh->default_only && (! use_default))
1297 0 : continue;
1298 0 : if (! prefix_match (rh,
1299 : url,
1300 : prefix_strlen,
1301 : infix_url,
1302 : infix_strlen,
1303 : suffix_url,
1304 : suffix_strlen))
1305 0 : continue;
1306 0 : url_found = true;
1307 0 : if (0 == strcasecmp (method,
1308 : MHD_HTTP_METHOD_OPTIONS))
1309 : {
1310 0 : return TALER_MHD_reply_cors_preflight (connection);
1311 : }
1312 0 : if ( (rh->method != NULL) &&
1313 0 : (0 != strcasecmp (method,
1314 : rh->method)) )
1315 0 : continue;
1316 0 : hc->rh = rh;
1317 0 : break;
1318 : }
1319 : /* Handle HTTP 405: METHOD NOT ALLOWED case */
1320 0 : if ( (NULL == hc->rh) &&
1321 : (url_found) )
1322 : {
1323 : struct MHD_Response *reply;
1324 : MHD_RESULT ret;
1325 0 : char *allowed = NULL;
1326 :
1327 0 : GNUNET_break_op (0);
1328 : /* compute 'Allowed:' header (required by HTTP spec for 405 replies) */
1329 0 : for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
1330 : {
1331 0 : struct TMH_RequestHandler *rh = &handlers[i];
1332 :
1333 0 : if (rh->default_only && (! use_default))
1334 0 : continue;
1335 0 : if (! prefix_match (rh,
1336 : url,
1337 : prefix_strlen,
1338 : infix_url,
1339 : infix_strlen,
1340 : suffix_url,
1341 : suffix_strlen))
1342 0 : continue;
1343 0 : if (NULL == allowed)
1344 : {
1345 0 : allowed = GNUNET_strdup (rh->method);
1346 : }
1347 : else
1348 : {
1349 : char *tmp;
1350 :
1351 0 : GNUNET_asprintf (&tmp,
1352 : "%s, %s",
1353 : allowed,
1354 : rh->method);
1355 0 : GNUNET_free (allowed);
1356 0 : allowed = tmp;
1357 : }
1358 0 : if (0 == strcasecmp (rh->method,
1359 : MHD_HTTP_METHOD_GET))
1360 : {
1361 : char *tmp;
1362 :
1363 0 : GNUNET_asprintf (&tmp,
1364 : "%s, %s",
1365 : allowed,
1366 : MHD_HTTP_METHOD_HEAD);
1367 0 : GNUNET_free (allowed);
1368 0 : allowed = tmp;
1369 : }
1370 : }
1371 0 : reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
1372 : method);
1373 0 : GNUNET_break (MHD_YES ==
1374 : MHD_add_response_header (reply,
1375 : MHD_HTTP_HEADER_ALLOW,
1376 : allowed));
1377 0 : GNUNET_free (allowed);
1378 0 : ret = MHD_queue_response (connection,
1379 : MHD_HTTP_METHOD_NOT_ALLOWED,
1380 : reply);
1381 0 : MHD_destroy_response (reply);
1382 0 : return ret;
1383 : }
1384 0 : if (NULL == hc->rh)
1385 : {
1386 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1387 : "Endpoint `%s' not known\n",
1388 : hc->url);
1389 0 : return TALER_MHD_reply_with_error (connection,
1390 : MHD_HTTP_NOT_FOUND,
1391 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
1392 : hc->url);
1393 : }
1394 : }
1395 : }
1396 : /* At this point, we must have found a handler */
1397 0 : GNUNET_assert (NULL != hc->rh);
1398 :
1399 : /* If an instance should be there, check one exists */
1400 0 : if ( (NULL == hc->instance) &&
1401 0 : (! hc->rh->skip_instance) )
1402 : {
1403 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1404 : "Instance `%s' not known\n",
1405 : hc->infix);
1406 0 : return TALER_MHD_reply_with_error (connection,
1407 : MHD_HTTP_NOT_FOUND,
1408 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
1409 0 : hc->infix);
1410 : }
1411 :
1412 : /* Access control for non-public handlers */
1413 0 : if (public_handlers != handlers)
1414 : {
1415 : const char *auth;
1416 : bool auth_ok;
1417 0 : bool auth_malformed = false;
1418 :
1419 : /* PATCHing an instance can alternatively be checked against
1420 : the default instance */
1421 0 : auth = MHD_lookup_connection_value (connection,
1422 : MHD_HEADER_KIND,
1423 : MHD_HTTP_HEADER_AUTHORIZATION);
1424 0 : if (NULL != auth)
1425 : {
1426 : /* We _only_ complain about malformed auth headers if
1427 : authorization was truly required (#6737). This helps
1428 : in case authorization was disabled in the backend
1429 : because some reverse proxy is already doing it, and
1430 : then that reverse proxy may forward malformed auth
1431 : headers to the backend. */
1432 0 : extract_token (&auth);
1433 0 : if (NULL == auth)
1434 0 : auth_malformed = true;
1435 0 : hc->auth_token = auth;
1436 : }
1437 :
1438 : /* If we have zero configured instances (not even ones that have been
1439 : purged) AND no override credentials, THEN we accept anything (no access
1440 : control), as we then also have no data to protect. */
1441 0 : auth_ok = ( (0 ==
1442 0 : GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) &&
1443 0 : (NULL == TMH_default_auth) );
1444 : /* Check against selected instance, if we have one */
1445 0 : if (NULL != hc->instance)
1446 0 : auth_ok |= (GNUNET_OK ==
1447 0 : TMH_check_auth (auth,
1448 0 : &hc->instance->auth.auth_salt,
1449 0 : &hc->instance->auth.auth_hash));
1450 : else /* Are the credentials provided OK for CLI override? */
1451 0 : auth_ok |= (use_default &&
1452 0 : (NULL != TMH_default_auth) &&
1453 0 : (NULL != auth) &&
1454 0 : (! auth_malformed) &&
1455 0 : (0 == strcmp (auth,
1456 : TMH_default_auth)) );
1457 0 : if (! auth_ok)
1458 : {
1459 0 : if (auth_malformed)
1460 0 : return TALER_MHD_reply_with_error (connection,
1461 : MHD_HTTP_UNAUTHORIZED,
1462 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1463 : "'" RFC_8959_PREFIX
1464 : "' prefix or 'Bearer' missing in 'Authorization' header");
1465 0 : return TALER_MHD_reply_with_error (connection,
1466 : MHD_HTTP_UNAUTHORIZED,
1467 : TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
1468 : "Check 'Authorization' header");
1469 : }
1470 : } /* if (use_private) */
1471 :
1472 :
1473 0 : if ( (NULL == hc->instance) &&
1474 0 : (! hc->rh->skip_instance) )
1475 : {
1476 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1477 : "Instance for URL `%s' not known\n",
1478 : url);
1479 0 : return TALER_MHD_reply_with_error (connection,
1480 : MHD_HTTP_NOT_FOUND,
1481 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
1482 : url);
1483 : }
1484 0 : if ( (NULL != hc->instance) && /* make static analysis happy */
1485 0 : (! hc->rh->skip_instance) &&
1486 0 : (hc->instance->deleted) &&
1487 0 : (! hc->rh->allow_deleted_instance) )
1488 : {
1489 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1490 : "Instance `%s' was deleted\n",
1491 : hc->instance->settings.id);
1492 0 : return TALER_MHD_reply_with_error (connection,
1493 : MHD_HTTP_NOT_FOUND,
1494 : TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED,
1495 0 : hc->instance->settings.id);
1496 : }
1497 : /* parse request body */
1498 0 : hc->has_body = ( (0 == strcasecmp (method,
1499 0 : MHD_HTTP_METHOD_POST)) ||
1500 : /* PUT is not yet used */
1501 0 : (0 == strcasecmp (method,
1502 : MHD_HTTP_METHOD_PATCH)) );
1503 0 : if (hc->has_body)
1504 : {
1505 : const char *cl;
1506 :
1507 : /* Maybe check for maximum upload size
1508 : and refuse requests if they are just too big. */
1509 0 : cl = MHD_lookup_connection_value (connection,
1510 : MHD_HEADER_KIND,
1511 : MHD_HTTP_HEADER_CONTENT_LENGTH);
1512 0 : if (NULL != cl)
1513 : {
1514 : unsigned long long cv;
1515 0 : size_t mul = hc->rh->max_upload;
1516 : char dummy;
1517 :
1518 0 : if (0 == mul)
1519 0 : mul = DEFAULT_MAX_UPLOAD_SIZE;
1520 0 : if (1 != sscanf (cl,
1521 : "%llu%c",
1522 : &cv,
1523 : &dummy))
1524 : {
1525 : /* Not valid HTTP request, just close connection. */
1526 0 : GNUNET_break_op (0);
1527 0 : return MHD_NO;
1528 : }
1529 0 : if (cv > mul)
1530 : {
1531 0 : GNUNET_break_op (0);
1532 0 : return TALER_MHD_reply_with_error (connection,
1533 : MHD_HTTP_PAYLOAD_TOO_LARGE,
1534 : TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT,
1535 : cl);
1536 : }
1537 : }
1538 0 : GNUNET_break (NULL == hc->request_body); /* can't have it already */
1539 : }
1540 0 : return MHD_YES; /* wait for MHD to call us again */
1541 : }
1542 :
1543 :
1544 : /**
1545 : * Function called during startup to add all known instances to our
1546 : * hash map in memory for faster lookups when we receive requests.
1547 : *
1548 : * @param cls closure, NULL, unused
1549 : * @param merchant_pub public key of the instance
1550 : * @param merchant_priv private key of the instance, NULL if not available
1551 : * @param is detailed configuration settings for the instance
1552 : * @param ias authentication settings for the instance
1553 : * @param accounts_length length of the @a accounts array
1554 : * @param accounts list of accounts of the merchant
1555 : */
1556 : static void
1557 0 : add_instance_cb (void *cls,
1558 : const struct TALER_MerchantPublicKeyP *merchant_pub,
1559 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
1560 : const struct TALER_MERCHANTDB_InstanceSettings *is,
1561 : const struct TALER_MERCHANTDB_InstanceAuthSettings *ias,
1562 : unsigned int accounts_length,
1563 : const struct TALER_MERCHANTDB_AccountDetails accounts[])
1564 : {
1565 : struct TMH_MerchantInstance *mi;
1566 :
1567 : (void) cls;
1568 0 : mi = TMH_lookup_instance (is->id);
1569 0 : if (NULL != mi)
1570 : {
1571 : /* (outdated) entry exists, remove old entry */
1572 0 : (void) TMH_instance_free_cb (NULL,
1573 0 : &mi->h_instance,
1574 : mi);
1575 : }
1576 :
1577 0 : mi = GNUNET_new (struct TMH_MerchantInstance);
1578 0 : mi->settings = *is;
1579 0 : mi->auth = *ias;
1580 0 : mi->settings.id = GNUNET_strdup (mi->settings.id);
1581 0 : mi->settings.name = GNUNET_strdup (mi->settings.name);
1582 0 : if (NULL != mi->settings.email)
1583 0 : mi->settings.email = GNUNET_strdup (mi->settings.email);
1584 0 : if (NULL != mi->settings.website)
1585 0 : mi->settings.website = GNUNET_strdup (mi->settings.website);
1586 0 : if (NULL != mi->settings.logo)
1587 0 : mi->settings.logo = GNUNET_strdup (mi->settings.logo);
1588 0 : mi->settings.address = json_incref (mi->settings.address);
1589 0 : mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
1590 0 : if (NULL != merchant_priv)
1591 0 : mi->merchant_priv = *merchant_priv;
1592 : else
1593 0 : mi->deleted = true;
1594 0 : mi->merchant_pub = *merchant_pub;
1595 0 : for (unsigned int i = 0; i<accounts_length; i++)
1596 : {
1597 0 : const struct TALER_MERCHANTDB_AccountDetails *acc = &accounts[i];
1598 : struct TMH_WireMethod *wm;
1599 :
1600 0 : wm = GNUNET_new (struct TMH_WireMethod);
1601 0 : wm->h_wire = acc->h_wire;
1602 0 : wm->payto_uri = GNUNET_strdup (acc->payto_uri);
1603 0 : wm->wire_salt = acc->salt;
1604 0 : wm->wire_method = TALER_payto_get_method (acc->payto_uri);
1605 0 : wm->active = acc->active;
1606 0 : GNUNET_CONTAINER_DLL_insert (mi->wm_head,
1607 : mi->wm_tail,
1608 : wm);
1609 : }
1610 0 : GNUNET_assert (GNUNET_OK ==
1611 : TMH_add_instance (mi));
1612 0 : }
1613 :
1614 :
1615 : /**
1616 : * Trigger (re)loading of instance settings from DB.
1617 : *
1618 : * @param cls NULL
1619 : * @param extra ID of the instance that changed, NULL
1620 : * to load all instances (will not handle purges!)
1621 : * @param extra_len number of bytes in @a extra
1622 : */
1623 : static void
1624 0 : load_instances (void *cls,
1625 : const void *extra,
1626 : size_t extra_len)
1627 : {
1628 : enum GNUNET_DB_QueryStatus qs;
1629 0 : const char *id = extra;
1630 :
1631 : (void) cls;
1632 : (void) extra;
1633 : (void) extra_len;
1634 0 : if ( (NULL != extra) &&
1635 0 : ( (0 == extra_len) ||
1636 0 : ('\0' != id[extra_len - 1]) ) )
1637 : {
1638 0 : GNUNET_break (0 == extra_len);
1639 0 : extra = NULL;
1640 : }
1641 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1642 : "Received instance settings notification: reload `%s'\n",
1643 : id);
1644 0 : if (NULL == extra)
1645 : {
1646 0 : qs = TMH_db->lookup_instances (TMH_db->cls,
1647 : false,
1648 : &add_instance_cb,
1649 : NULL);
1650 : }
1651 : else
1652 : {
1653 : struct TMH_MerchantInstance *mi;
1654 :
1655 : /* This must be done here to handle instance
1656 : purging, as for purged instances, the DB
1657 : lookup below will otherwise do nothing */
1658 0 : mi = TMH_lookup_instance (id);
1659 0 : if (NULL != mi)
1660 : {
1661 0 : (void) TMH_instance_free_cb (NULL,
1662 0 : &mi->h_instance,
1663 : mi);
1664 : }
1665 0 : qs = TMH_db->lookup_instance (TMH_db->cls,
1666 : id,
1667 : false,
1668 : &add_instance_cb,
1669 : NULL);
1670 : }
1671 0 : if (0 > qs)
1672 : {
1673 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1674 : "Failed initialization. Check database setup.\n");
1675 0 : result = EXIT_FAILURE;
1676 0 : GNUNET_SCHEDULER_shutdown ();
1677 0 : return;
1678 : }
1679 : }
1680 :
1681 :
1682 : /**
1683 : * A transaction modified an instance setting
1684 : * (or created/deleted/purged one). Notify all
1685 : * backends about the change.
1686 : *
1687 : * @param id ID of the instance that changed
1688 : */
1689 : void
1690 0 : TMH_reload_instances (const char *id)
1691 : {
1692 0 : struct GNUNET_DB_EventHeaderP es = {
1693 0 : .size = ntohs (sizeof (es)),
1694 0 : .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
1695 : };
1696 :
1697 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1698 : "Generating instance settings notification: reload `%s'\n",
1699 : id);
1700 0 : TMH_db->event_notify (TMH_db->cls,
1701 : &es,
1702 : id,
1703 : (NULL == id)
1704 : ? 0
1705 0 : : strlen (id) + 1);
1706 0 : }
1707 :
1708 :
1709 : /**
1710 : * Main function that will be run by the scheduler.
1711 : *
1712 : * @param cls closure
1713 : * @param args remaining command-line arguments
1714 : * @param cfgfile name of the configuration file used (for saving, can be
1715 : * NULL!)
1716 : * @param config configuration
1717 : */
1718 : static void
1719 0 : run (void *cls,
1720 : char *const *args,
1721 : const char *cfgfile,
1722 : const struct GNUNET_CONFIGURATION_Handle *config)
1723 : {
1724 : int fh;
1725 : enum TALER_MHD_GlobalOptions go;
1726 : int elen;
1727 : int alen;
1728 : const char *tok;
1729 :
1730 : (void) cls;
1731 : (void) args;
1732 : (void) cfgfile;
1733 0 : tok = getenv ("TALER_MERCHANT_TOKEN");
1734 0 : if ( (NULL != tok) &&
1735 0 : (NULL == TMH_default_auth) )
1736 0 : TMH_default_auth = GNUNET_strdup (tok);
1737 0 : if ( (NULL != TMH_default_auth) &&
1738 0 : (0 != strncmp (TMH_default_auth,
1739 : RFC_8959_PREFIX,
1740 : strlen (RFC_8959_PREFIX))) )
1741 : {
1742 : char *tmp;
1743 :
1744 0 : GNUNET_asprintf (&tmp,
1745 : "%s%s",
1746 : RFC_8959_PREFIX,
1747 : TMH_default_auth);
1748 0 : GNUNET_free (TMH_default_auth);
1749 0 : TMH_default_auth = tmp;
1750 : }
1751 0 : cfg = config;
1752 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1753 : "Starting taler-merchant-httpd\n");
1754 0 : go = TALER_MHD_GO_NONE;
1755 0 : if (merchant_connection_close)
1756 0 : go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
1757 0 : TALER_MHD_setup (go);
1758 :
1759 0 : result = GNUNET_SYSERR;
1760 0 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1761 : NULL);
1762 0 : if (GNUNET_OK !=
1763 0 : TALER_config_get_currency (cfg,
1764 : &TMH_currency))
1765 : {
1766 0 : GNUNET_SCHEDULER_shutdown ();
1767 0 : return;
1768 : }
1769 0 : if (GNUNET_OK !=
1770 0 : GNUNET_CONFIGURATION_get_value_time (cfg,
1771 : "merchant",
1772 : "LEGAL_PRESERVATION",
1773 : &TMH_legal_expiration))
1774 : {
1775 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1776 : "merchant",
1777 : "LEGAL_PRESERVATION");
1778 0 : GNUNET_SCHEDULER_shutdown ();
1779 0 : return;
1780 : }
1781 0 : if (GNUNET_YES ==
1782 0 : GNUNET_CONFIGURATION_get_value_yesno (cfg,
1783 : "merchant",
1784 : "FORCE_AUDIT"))
1785 0 : TMH_force_audit = GNUNET_YES;
1786 0 : TALER_TEMPLATING_init ("merchant");
1787 0 : if (GNUNET_OK !=
1788 0 : TMH_spa_init ())
1789 : {
1790 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1791 : "Failed to load single page app\n");
1792 0 : GNUNET_SCHEDULER_shutdown ();
1793 0 : return;
1794 : }
1795 0 : TMH_statics_init ();
1796 0 : elen = TMH_EXCHANGES_init (config);
1797 0 : if (GNUNET_SYSERR == elen)
1798 : {
1799 0 : GNUNET_SCHEDULER_shutdown ();
1800 0 : return;
1801 : }
1802 0 : alen = TMH_AUDITORS_init (config);
1803 0 : if (GNUNET_SYSERR == alen)
1804 : {
1805 0 : GNUNET_SCHEDULER_shutdown ();
1806 0 : return;
1807 : }
1808 0 : if (0 == elen + alen)
1809 : {
1810 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1811 : "Fatal: no trusted exchanges and no trusted auditors configured. Exiting.\n");
1812 0 : GNUNET_SCHEDULER_shutdown ();
1813 0 : return;
1814 : }
1815 0 : if (NULL ==
1816 0 : (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4,
1817 : GNUNET_YES)))
1818 : {
1819 0 : GNUNET_SCHEDULER_shutdown ();
1820 0 : return;
1821 : }
1822 0 : if (NULL ==
1823 0 : (TMH_db = TALER_MERCHANTDB_plugin_load (cfg)))
1824 : {
1825 0 : GNUNET_SCHEDULER_shutdown ();
1826 0 : return;
1827 : }
1828 0 : if (GNUNET_OK !=
1829 0 : TMH_db->connect (TMH_db->cls))
1830 : {
1831 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1832 : "Failed to initialize database connection\n");
1833 0 : GNUNET_SCHEDULER_shutdown ();
1834 0 : return;
1835 : }
1836 : {
1837 0 : struct GNUNET_DB_EventHeaderP es = {
1838 0 : .size = ntohs (sizeof (es)),
1839 0 : .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
1840 : };
1841 :
1842 0 : instance_eh = TMH_db->event_listen (TMH_db->cls,
1843 : &es,
1844 0 : GNUNET_TIME_UNIT_FOREVER_REL,
1845 : &load_instances,
1846 : NULL);
1847 : }
1848 0 : load_instances (NULL,
1849 : NULL,
1850 : 0);
1851 : /* start watching reserves */
1852 0 : TMH_RESERVES_init ();
1853 0 : fh = TALER_MHD_bind (cfg,
1854 : "merchant",
1855 : &port);
1856 0 : if ( (0 == port) &&
1857 : (-1 == fh) )
1858 : {
1859 0 : GNUNET_SCHEDULER_shutdown ();
1860 0 : return;
1861 : }
1862 :
1863 : {
1864 : struct MHD_Daemon *mhd;
1865 :
1866 0 : mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK
1867 : | MHD_USE_AUTO,
1868 : port,
1869 : NULL, NULL,
1870 : &url_handler, NULL,
1871 : MHD_OPTION_LISTEN_SOCKET, fh,
1872 : MHD_OPTION_NOTIFY_COMPLETED,
1873 : &handle_mhd_completion_callback, NULL,
1874 : MHD_OPTION_CONNECTION_TIMEOUT,
1875 : (unsigned int) 10 /* 10s */,
1876 : MHD_OPTION_END);
1877 0 : if (NULL == mhd)
1878 : {
1879 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1880 : "Failed to launch HTTP service. Is the port in use?\n");
1881 0 : GNUNET_SCHEDULER_shutdown ();
1882 0 : return;
1883 : }
1884 0 : result = GNUNET_OK;
1885 0 : TALER_MHD_daemon_start (mhd);
1886 : }
1887 : }
1888 :
1889 :
1890 : /**
1891 : * The main function of the serve tool
1892 : *
1893 : * @param argc number of arguments from the command line
1894 : * @param argv command line arguments
1895 : * @return 0 ok, non-zero on error
1896 : */
1897 : int
1898 14 : main (int argc,
1899 : char *const *argv)
1900 : {
1901 : enum GNUNET_GenericReturnValue res;
1902 14 : struct GNUNET_GETOPT_CommandLineOption options[] = {
1903 14 : GNUNET_GETOPT_option_flag ('C',
1904 : "connection-close",
1905 : "force HTTP connections to be closed after each request",
1906 : &merchant_connection_close),
1907 14 : GNUNET_GETOPT_option_timetravel ('T',
1908 : "timetravel"),
1909 14 : GNUNET_GETOPT_option_string ('a',
1910 : "auth",
1911 : "TOKEN",
1912 : "use TOKEN to initially authenticate access to the default instance (you can also set the TALER_MERCHANT_TOKEN environment variable instead)",
1913 : &TMH_default_auth),
1914 14 : GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION),
1915 : GNUNET_GETOPT_OPTION_END
1916 : };
1917 :
1918 14 : TALER_OS_init ();
1919 14 : res = GNUNET_PROGRAM_run (argc, argv,
1920 : "taler-merchant-httpd",
1921 : "Taler merchant's HTTP backend interface",
1922 : options,
1923 : &run, NULL);
1924 14 : if (GNUNET_SYSERR == res)
1925 0 : return EXIT_INVALIDARGUMENT;
1926 14 : if (GNUNET_NO == res)
1927 14 : return EXIT_SUCCESS;
1928 0 : return (GNUNET_OK == result) ? EXIT_SUCCESS : 1;
1929 : }
|