exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-auditor-httpd.c (54557B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2024 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 /**
     18  * @file taler-auditor-httpd.c
     19  * @brief Serve the HTTP interface of the auditor
     20  * @defgroup request Request handling routines
     21  * @author Florian Dold
     22  * @author Benedikt Mueller
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <jansson.h>
     28 #include <microhttpd.h>
     29 #include <pthread.h>
     30 #include <sys/resource.h>
     31 #include "taler/taler_mhd_lib.h"
     32 #include "auditordb_lib.h"
     33 #include "exchangedb_lib.h"
     34 #include "taler-auditor-httpd_spa.h"
     35 #include "taler-auditor-httpd_put-deposit-confirmation.h"
     36 #include "taler-auditor-httpd_get-monitoring-deposit-confirmations.h"
     37 #include "taler-auditor-httpd_get-monitoring-amount-arithmetic-inconsistency.h"
     38 #include "taler-auditor-httpd_get-monitoring-coin-inconsistency.h"
     39 #include "taler-auditor-httpd_get-monitoring-row-inconsistency.h"
     40 #include "taler-auditor-httpd_get-monitoring-emergency.h"
     41 #include "taler-auditor-httpd_get-monitoring-emergency-by-count.h"
     42 #include "taler-auditor-httpd_get-monitoring-early-aggregation.h"
     43 #include                                                                \
     44   "taler-auditor-httpd_get-monitoring-denomination-key-validity-withdraw-inconsistency.h"
     45 #include "taler-auditor-httpd_get-monitoring-purse-not-closed-inconsistencies.h"
     46 #include \
     47   "taler-auditor-httpd_get-monitoring-reserve-balance-insufficient-inconsistency.h"
     48 #include "taler-auditor-httpd_get-monitoring-bad-sig-losses.h"
     49 #include "taler-auditor-httpd_get-monitoring-aml-holds.h"
     50 #include "taler-auditor-httpd_get-monitoring-closure-lags.h"
     51 #include "taler-auditor-httpd_mhd.h"
     52 #include "taler-auditor-httpd.h"
     53 #include "taler-auditor-httpd_delete-generic.h"
     54 #include "taler-auditor-httpd_patch-generic-suppressed.h"
     55 #include "taler-auditor-httpd_get-monitoring-kycauth-in-inconsistency.h"
     56 #include "taler-auditor-httpd_get-monitoring-reserve-in-inconsistency.h"
     57 #include "taler-auditor-httpd_get-monitoring-reserve-not-closed-inconsistency.h"
     58 #include "taler-auditor-httpd_get-monitoring-denominations-without-sigs.h"
     59 #include "taler-auditor-httpd_get-monitoring-misattribution-in-inconsistency.h"
     60 #include "taler-auditor-httpd_get-monitoring-reserves.h"
     61 #include "taler-auditor-httpd_get-monitoring-pending-deposits.h"
     62 #include "taler-auditor-httpd_get-monitoring-purses.h"
     63 #include "taler-auditor-httpd_get-monitoring-historic-denomination-revenue.h"
     64 #include "taler-auditor-httpd_get-monitoring-historic-reserve-summary.h"
     65 #include "taler-auditor-httpd_get-monitoring-wire-format-inconsistency.h"
     66 #include "taler-auditor-httpd_get-monitoring-wire-out-inconsistency.h"
     67 #include \
     68   "taler-auditor-httpd_get-monitoring-reserve-balance-summary-wrong-inconsistency.h"
     69 #include "taler-auditor-httpd_get-monitoring-row-minor-inconsistencies.h"
     70 #include "taler-auditor-httpd_get-monitoring-fee-time-inconsistency.h"
     71 #include "taler-auditor-httpd_get-monitoring-balances.h"
     72 #include "taler-auditor-httpd_get-monitoring-progress.h"
     73 #include "exchange-database/preflight.h"
     74 
     75 /**
     76  * Auditor protocol version string.
     77  *
     78  * Taler protocol version in the format CURRENT:REVISION:AGE
     79  * as used by GNU libtool.  See
     80  * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
     81  *
     82  * Please be very careful when updating and follow
     83  * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
     84  * precisely.  Note that this version has NOTHING to do with the
     85  * release version, and the format is NOT the same that semantic
     86  * versioning uses either.
     87  */
     88 #define AUDITOR_PROTOCOL_VERSION "2:0:0"
     89 
     90 /**
     91  * Salt we use when doing the KDF for access.
     92  */
     93 #define KDF_SALT "auditor-standard-auth"
     94 
     95 /**
     96  * Backlog for listen operation on unix domain sockets.
     97  */
     98 #define UNIX_BACKLOG 500
     99 
    100 /**
    101  * Should we return "Connection: close" in each response?
    102  */
    103 static int auditor_connection_close;
    104 
    105 /**
    106  * The auditor's configuration.
    107  */
    108 static const struct GNUNET_CONFIGURATION_Handle *cfg;
    109 
    110 /**
    111  * Our auditor database context.
    112  */
    113 struct TALER_AUDITORDB_PostgresContext *TAH_apg;
    114 
    115 /**
    116  * Our exchange database context.
    117  */
    118 struct TALER_EXCHANGEDB_PostgresContext *TAH_epg;
    119 
    120 /**
    121  * Public key of this auditor.
    122  */
    123 static struct TALER_AuditorPublicKeyP auditor_pub;
    124 
    125 /**
    126  * Exchange master public key (according to the
    127  * configuration).  (global)
    128  */
    129 struct TALER_MasterPublicKeyP TAH_master_public_key;
    130 
    131 /**
    132  * Exchange master public key (according to the
    133  * configuration).  (global)
    134  */
    135 struct TALER_MasterPublicKeyP TAH_master_public_key;
    136 
    137 char *TAH_spa_dir;
    138 
    139 /**
    140  * Default timeout in seconds for HTTP requests.
    141  */
    142 static unsigned int connection_timeout = 30;
    143 
    144 /**
    145  * Return value from main()
    146  */
    147 static int global_ret;
    148 
    149 /**
    150  * Disables authentication checks.
    151  */
    152 static int disable_auth;
    153 
    154 /**
    155  * True if we started any HTTP daemon.
    156  */
    157 static bool have_daemons;
    158 
    159 /**
    160  * Our currency.
    161  */
    162 char *TAH_currency;
    163 
    164 /**
    165  * Authorization code to use.
    166  */
    167 static struct GNUNET_HashCode TAH_auth;
    168 
    169 /**
    170  * Prefix required for the access token.
    171  */
    172 #define RFC_8959_PREFIX "secret-token:"
    173 
    174 
    175 /**
    176  * Function called whenever MHD is done with a request.  If the
    177  * request was a POST, we may have stored a `struct Buffer *` in the
    178  * @a con_cls that might still need to be cleaned up.  Call the
    179  * respective function to free the memory.
    180  *
    181  * @param cls client-defined closure
    182  * @param connection connection handle
    183  * @param con_cls value as set by the last call to
    184  *        the #MHD_AccessHandlerCallback
    185  * @param toe reason for request termination
    186  * @see #MHD_OPTION_NOTIFY_COMPLETED
    187  * @ingroup request
    188  */
    189 static void
    190 handle_mhd_completion_callback (void *cls,
    191                                 struct MHD_Connection *connection,
    192                                 void **con_cls,
    193                                 enum MHD_RequestTerminationCode toe)
    194 {
    195   (void) cls;
    196   (void) connection;
    197   (void) toe;
    198   if (NULL == *con_cls)
    199     return;
    200   TALER_MHD_parse_post_cleanup_callback (*con_cls);
    201   *con_cls = NULL;
    202 }
    203 
    204 
    205 /**
    206  * Handle a "/config" request.
    207  *
    208  * @param rh context of the handler
    209  * @param connection the MHD connection to handle
    210  * @param[in,out] connection_cls the connection's closure (can be updated)
    211  * @param upload_data upload data
    212  * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
    213  * @param args NULL-terminated array of remaining parts of the URI broken up at '/'
    214  * @return MHD result code
    215  */
    216 static enum MHD_Result
    217 handle_config (struct TAH_RequestHandler *rh,
    218                struct MHD_Connection *connection,
    219                void **connection_cls,
    220                const char *upload_data,
    221                size_t *upload_data_size,
    222                const char *const args[])
    223 {
    224   static json_t *ver; /* we build the response only once, keep around for next query! */
    225 
    226   (void) rh;
    227   (void) upload_data;
    228   (void) upload_data_size;
    229   (void) connection_cls;
    230   if (NULL == ver)
    231   {
    232     ver = GNUNET_JSON_PACK (
    233       GNUNET_JSON_pack_string ("name",
    234                                "taler-auditor"),
    235       GNUNET_JSON_pack_string ("version",
    236                                AUDITOR_PROTOCOL_VERSION),
    237       GNUNET_JSON_pack_string ("build_version",
    238                                PACKAGE_VERSION),
    239       GNUNET_JSON_pack_string ("implementation",
    240                                "urn:net:taler:specs:taler-auditor:c-reference"),
    241       GNUNET_JSON_pack_string ("currency",
    242                                TAH_currency),
    243       GNUNET_JSON_pack_data_auto ("auditor_public_key",
    244                                   &auditor_pub),
    245       GNUNET_JSON_pack_data_auto ("exchange_master_public_key",
    246                                   &TAH_master_public_key));
    247   }
    248   if (NULL == ver)
    249   {
    250     GNUNET_break (0);
    251     return MHD_NO;
    252   }
    253   return TALER_MHD_reply_json (connection,
    254                                ver,
    255                                MHD_HTTP_OK);
    256 }
    257 
    258 
    259 /**
    260  * Extract the token from authorization header value @a auth.
    261  *
    262  * @param auth pointer to authorization header value,
    263  *        will be updated to point to the start of the token
    264  *        or set to NULL if header value is invalid
    265  */
    266 static void
    267 extract_token (const char **auth)
    268 {
    269   const char *bearer = "Bearer ";
    270   const char *tok = *auth;
    271 
    272   if (0 != strncmp (tok,
    273                     bearer,
    274                     strlen (bearer)))
    275   {
    276     *auth = NULL;
    277     return;
    278   }
    279   tok += strlen (bearer);
    280   while (' ' == *tok)
    281     tok++;
    282   if (0 != strncasecmp (tok,
    283                         RFC_8959_PREFIX,
    284                         strlen (RFC_8959_PREFIX)))
    285   {
    286     *auth = NULL;
    287     return;
    288   }
    289   *auth = tok;
    290 }
    291 
    292 
    293 static enum GNUNET_GenericReturnValue
    294 check_auth (const char *token)
    295 {
    296   struct GNUNET_HashCode val;
    297 
    298   if (NULL == token)
    299     return GNUNET_SYSERR;
    300   token += strlen (RFC_8959_PREFIX);
    301   GNUNET_assert (GNUNET_YES ==
    302                  GNUNET_CRYPTO_hkdf_gnunet (
    303                    &val,
    304                    sizeof (val),
    305                    KDF_SALT,
    306                    strlen (KDF_SALT),
    307                    token,
    308                    strlen (token)));
    309   /* We compare hashes instead of directly comparing
    310      tokens to minimize side-channel attacks on token length */
    311   return (0 ==
    312           GNUNET_memcmp_priv (&val,
    313                               &TAH_auth))
    314            ? GNUNET_OK
    315            : GNUNET_SYSERR;
    316 }
    317 
    318 
    319 /**
    320  * Handle incoming HTTP request.
    321  *
    322  * @param cls closure for MHD daemon (unused)
    323  * @param connection the connection
    324  * @param url the requested url
    325  * @param method the method (POST, GET, ...)
    326  * @param version HTTP version (ignored)
    327  * @param upload_data request data
    328  * @param upload_data_size size of @a upload_data in bytes
    329  * @param con_cls closure for request (a `struct Buffer *`)
    330  * @return MHD result code
    331  */
    332 static enum MHD_Result
    333 handle_mhd_request (void *cls,
    334                     struct MHD_Connection *connection,
    335                     const char *url,
    336                     const char *method,
    337                     const char *version,
    338                     const char *upload_data,
    339                     size_t *upload_data_size,
    340                     void **con_cls)
    341 {
    342   static struct TAH_RequestHandler handlers[] = {
    343     /* Our most popular handler (thus first!), used by merchants to
    344        probabilistically report us their deposit confirmations. */
    345     { .url = "/deposit-confirmation",
    346       .method = MHD_HTTP_METHOD_PUT,
    347       .mime_type = "application/json",
    348       .handler = &TAH_put_deposit_confirmation,
    349       .response_code = MHD_HTTP_NO_CONTENT},
    350     { .url = "/webui",
    351       .method = MHD_HTTP_METHOD_GET,
    352       .handler = &TAH_spa_handler},
    353     { .url = "/monitoring/deposit-confirmations",
    354       .method = MHD_HTTP_METHOD_GET,
    355       .mime_type = "application/json",
    356       .data = NULL,
    357       .data_size = 0,
    358       .handler = &TAH_get_monitoring_deposit_confirmations,
    359       .response_code = MHD_HTTP_OK,
    360       .requires_auth = true },
    361     { .url = "/monitoring/deposit-confirmations",
    362       .method = MHD_HTTP_METHOD_PATCH,
    363       .mime_type = "application/json",
    364       .handler = &TAH_patch_generic_suppressed,
    365       .response_code = MHD_HTTP_NO_CONTENT,
    366       .requires_auth = true,
    367       .table = TALER_AUDITORDB_DEPOSIT_CONFIRMATION },
    368     { .url = "/monitoring/pending-deposits",
    369       .method = MHD_HTTP_METHOD_GET,
    370       .mime_type = "application/json",
    371       .data = NULL,
    372       .data_size = 0,
    373       .handler = &TAH_get_monitoring_pending_deposits,
    374       .response_code = MHD_HTTP_OK,
    375       .requires_auth = true },
    376     { .url = "/monitoring/pending-deposits",
    377       .method = MHD_HTTP_METHOD_DELETE,
    378       .mime_type = "application/json",
    379       .handler = &TAH_delete_generic,
    380       .response_code = MHD_HTTP_NO_CONTENT,
    381       .requires_auth = true,
    382       .table = TALER_AUDITORDB_PENDING_DEPOSITS },
    383     { .url = "/monitoring/pending-deposits",
    384       .method = MHD_HTTP_METHOD_PATCH,
    385       .mime_type = "application/json",
    386       .handler = &TAH_patch_generic_suppressed,
    387       .response_code = MHD_HTTP_NO_CONTENT,
    388       .requires_auth = true,
    389       .table = TALER_AUDITORDB_PENDING_DEPOSITS },
    390     { .url = "/monitoring/early-aggregations",
    391       .method = MHD_HTTP_METHOD_GET,
    392       .mime_type = "application/json",
    393       .data = NULL,
    394       .data_size = 0,
    395       .handler = &TAH_get_monitoring_early_aggregation,
    396       .response_code = MHD_HTTP_OK,
    397       .requires_auth = true },
    398     { .url = "/monitoring/early-aggregations",
    399       .method = MHD_HTTP_METHOD_DELETE,
    400       .mime_type = "application/json",
    401       .handler = &TAH_delete_generic,
    402       .response_code = MHD_HTTP_NO_CONTENT,
    403       .requires_auth = true,
    404       .table = TALER_AUDITORDB_EARLY_AGGREGATIONS },
    405     { .url = "/monitoring/early-aggregations",
    406       .method = MHD_HTTP_METHOD_PATCH,
    407       .mime_type = "application/json",
    408       .handler = &TAH_patch_generic_suppressed,
    409       .response_code = MHD_HTTP_NO_CONTENT,
    410       .requires_auth = true,
    411       .table = TALER_AUDITORDB_EARLY_AGGREGATIONS },
    412     { .url = "/monitoring/deposit-confirmations",
    413       .method = MHD_HTTP_METHOD_DELETE,
    414       .mime_type = "application/json",
    415       .data = NULL,
    416       .data_size = 0,
    417       .handler = &TAH_delete_generic,
    418       .response_code = MHD_HTTP_OK,
    419       .requires_auth = true,
    420       .table = TALER_AUDITORDB_DEPOSIT_CONFIRMATION },
    421     { .url = "/monitoring/amount-arithmetic-inconsistencies",
    422       .method = MHD_HTTP_METHOD_GET,
    423       .mime_type = "application/json",
    424       .data = NULL,
    425       .data_size = 0,
    426       .handler = &TAH_get_monitoring_amount_arithmetic_inconsistency,
    427       .response_code = MHD_HTTP_OK,
    428       .requires_auth = true },
    429     { .url = "/monitoring/amount-arithmetic-inconsistencies",
    430       .method = MHD_HTTP_METHOD_DELETE,
    431       .mime_type = "application/json",
    432       .data = NULL,
    433       .data_size = 0,
    434       .handler = &TAH_delete_generic,
    435       .response_code = MHD_HTTP_OK,
    436       .requires_auth = true,
    437       .table = TALER_AUDITORDB_AMOUNT_ARITHMETIC_INCONSISTENCY },
    438     { .url = "/monitoring/amount-arithmetic-inconsistencies",
    439       .method = MHD_HTTP_METHOD_PATCH,
    440       .mime_type = "application/json",
    441       .data = NULL,
    442       .data_size = 0,
    443       .handler = &TAH_patch_generic_suppressed,
    444       .response_code = MHD_HTTP_OK,
    445       .requires_auth = true,
    446       .table = TALER_AUDITORDB_AMOUNT_ARITHMETIC_INCONSISTENCY },
    447     { .url = "/monitoring/coin-inconsistencies",
    448       .method = MHD_HTTP_METHOD_GET,
    449       .mime_type = "application/json",
    450       .data = NULL,
    451       .data_size = 0,
    452       .handler = &TAH_get_monitoring_coin_inconsistency,
    453       .response_code = MHD_HTTP_OK,
    454       .requires_auth = true },
    455     { .url = "/monitoring/coin-inconsistencies",
    456       .method = MHD_HTTP_METHOD_DELETE,
    457       .mime_type = "application/json",
    458       .data = NULL,
    459       .data_size = 0,
    460       .handler = &TAH_delete_generic,
    461       .response_code = MHD_HTTP_OK,
    462       .requires_auth = true,
    463       .table = TALER_AUDITORDB_COIN_INCONSISTENCY },
    464     { .url = "/monitoring/coin-inconsistencies",
    465       .method = MHD_HTTP_METHOD_PATCH,
    466       .mime_type = "application/json",
    467       .data = NULL,
    468       .data_size = 0,
    469       .handler = &TAH_patch_generic_suppressed,
    470       .response_code = MHD_HTTP_OK,
    471       .requires_auth = true,
    472       .table = TALER_AUDITORDB_COIN_INCONSISTENCY },
    473     { .url = "/monitoring/row-inconsistencies",
    474       .method = MHD_HTTP_METHOD_GET,
    475       .mime_type = "application/json",
    476       .data = NULL,
    477       .data_size = 0,
    478       .handler = &TAH_get_monitoring_row_inconsistency,
    479       .response_code = MHD_HTTP_OK,
    480       .requires_auth = true },
    481     { .url = "/monitoring/row-inconsistencies",
    482       .method = MHD_HTTP_METHOD_DELETE,
    483       .mime_type = "application/json",
    484       .data = NULL,
    485       .data_size = 0,
    486       .handler = &TAH_delete_generic,
    487       .response_code = MHD_HTTP_OK,
    488       .requires_auth = true,
    489       .table = TALER_AUDITORDB_ROW_INCONSISTENCY},
    490     { .url = "/monitoring/row-inconsistencies",
    491       .method = MHD_HTTP_METHOD_PATCH,
    492       .mime_type = "application/json",
    493       .data = NULL,
    494       .data_size = 0,
    495       .handler = &TAH_patch_generic_suppressed,
    496       .response_code = MHD_HTTP_OK,
    497       .requires_auth = true,
    498       .table = TALER_AUDITORDB_ROW_INCONSISTENCY },
    499     { .url = "/monitoring/bad-sig-losses",
    500       .method = MHD_HTTP_METHOD_GET,
    501       .mime_type = "application/json",
    502       .data = NULL,
    503       .data_size = 0,
    504       .handler = &TAH_get_monitoring_bad_sig_losses,
    505       .response_code = MHD_HTTP_OK,
    506       .requires_auth = true },
    507     { .url = "/monitoring/bad-sig-losses",
    508       .method = MHD_HTTP_METHOD_DELETE,
    509       .mime_type = "application/json",
    510       .data = NULL,
    511       .data_size = 0,
    512       .handler = &TAH_delete_generic,
    513       .response_code = MHD_HTTP_OK,
    514       .requires_auth = true,
    515       .table = TALER_AUDITORDB_BAD_SIG_LOSSES},
    516     { .url = "/monitoring/bad-sig-losses",
    517       .method = MHD_HTTP_METHOD_PATCH,
    518       .mime_type = "application/json",
    519       .data = NULL,
    520       .data_size = 0,
    521       .handler = &TAH_patch_generic_suppressed,
    522       .response_code = MHD_HTTP_OK,
    523       .requires_auth = true,
    524       .table = TALER_AUDITORDB_BAD_SIG_LOSSES },
    525     { .url = "/monitoring/aml-holds",
    526       .method = MHD_HTTP_METHOD_GET,
    527       .mime_type = "application/json",
    528       .data = NULL,
    529       .data_size = 0,
    530       .handler = &TAH_get_monitoring_aml_holds,
    531       .response_code = MHD_HTTP_OK,
    532       .requires_auth = true },
    533     { .url = "/monitoring/aml-holds",
    534       .method = MHD_HTTP_METHOD_DELETE,
    535       .mime_type = "application/json",
    536       .data = NULL,
    537       .data_size = 0,
    538       .handler = &TAH_delete_generic,
    539       .response_code = MHD_HTTP_OK,
    540       .requires_auth = true,
    541       .table = TALER_AUDITORDB_AML_HOLDS },
    542     { .url = "/monitoring/aml-holds",
    543       .method = MHD_HTTP_METHOD_PATCH,
    544       .mime_type = "application/json",
    545       .data = NULL,
    546       .data_size = 0,
    547       .handler = &TAH_patch_generic_suppressed,
    548       .response_code = MHD_HTTP_OK,
    549       .requires_auth = true,
    550       .table = TALER_AUDITORDB_AML_HOLDS },
    551     { .url = "/monitoring/closure-lags",
    552       .method = MHD_HTTP_METHOD_GET,
    553       .mime_type = "application/json",
    554       .data = NULL,
    555       .data_size = 0,
    556       .handler = &TAH_get_monitoring_closure_lags,
    557       .response_code = MHD_HTTP_OK,
    558       .requires_auth = true },
    559     { .url = "/monitoring/closure-lags",
    560       .method = MHD_HTTP_METHOD_DELETE,
    561       .mime_type = "application/json",
    562       .data = NULL,
    563       .data_size = 0,
    564       .handler = &TAH_delete_generic,
    565       .response_code = MHD_HTTP_OK,
    566       .requires_auth = true,
    567       .table = TALER_AUDITORDB_CLOSURE_LAGS },
    568     { .url = "/monitoring/closure-lags",
    569       .method = MHD_HTTP_METHOD_PATCH,
    570       .mime_type = "application/json",
    571       .data = NULL,
    572       .data_size = 0,
    573       .handler = &TAH_patch_generic_suppressed,
    574       .response_code = MHD_HTTP_OK,
    575       .requires_auth = true,
    576       .table = TALER_AUDITORDB_CLOSURE_LAGS },
    577     { .url = "/monitoring/emergencies",
    578       .method = MHD_HTTP_METHOD_GET,
    579       .mime_type = "application/json",
    580       .data = NULL,
    581       .data_size = 0,
    582       .handler = &TAH_get_monitoring_emergency,
    583       .response_code = MHD_HTTP_OK,
    584       .requires_auth = true },
    585     { .url = "/monitoring/emergencies",
    586       .method = MHD_HTTP_METHOD_DELETE,
    587       .mime_type = "application/json",
    588       .data = NULL,
    589       .data_size = 0,
    590       .handler = &TAH_delete_generic,
    591       .response_code = MHD_HTTP_OK,
    592       .requires_auth = true,
    593       .table = TALER_AUDITORDB_EMERGENCY },
    594     { .url = "/monitoring/emergencies",
    595       .method = MHD_HTTP_METHOD_PATCH,
    596       .mime_type = "application/json",
    597       .data = NULL,
    598       .data_size = 0,
    599       .handler = &TAH_patch_generic_suppressed,
    600       .response_code = MHD_HTTP_OK,
    601       .requires_auth = true,
    602       .table = TALER_AUDITORDB_EMERGENCY  },
    603     { .url = "/monitoring/denomination-key-validity-withdraw-inconsistencies",
    604       .method = MHD_HTTP_METHOD_GET,
    605       .mime_type = "application/json",
    606       .data = NULL,
    607       .data_size = 0,
    608       .handler =
    609         &TAH_get_monitoring_denomination_key_validity_withdraw_inconsistency,
    610       .response_code = MHD_HTTP_OK,
    611       .requires_auth = true },
    612     { .url = "/monitoring/denomination-key-validity-withdraw-inconsistencies",
    613       .method = MHD_HTTP_METHOD_DELETE,
    614       .mime_type = "application/json",
    615       .data = NULL,
    616       .data_size = 0,
    617       .handler = &TAH_delete_generic,
    618       .response_code = MHD_HTTP_OK,
    619       .requires_auth = true,
    620       .table = TALER_AUDITORDB_DENOMINATION_KEY_VALIDITY_WITHDRAW_INCONSISTENCY}
    621     ,
    622     { .url = "/monitoring/denomination-key-validity-withdraw-inconsistencies",
    623       .method = MHD_HTTP_METHOD_PATCH,
    624       .mime_type = "application/json",
    625       .data = NULL,
    626       .data_size = 0,
    627       .handler = &TAH_patch_generic_suppressed,
    628       .response_code = MHD_HTTP_OK,
    629       .requires_auth = true,
    630       .table = TALER_AUDITORDB_DENOMINATION_KEY_VALIDITY_WITHDRAW_INCONSISTENCY}
    631     ,
    632     { .url = "/monitoring/reserve-balance-insufficient-inconsistencies",
    633       .method = MHD_HTTP_METHOD_GET,
    634       .mime_type = "application/json",
    635       .data = NULL,
    636       .data_size = 0,
    637       .handler = &TAH_get_monitoring_reserve_balance_insufficient_inconsistency,
    638       .response_code = MHD_HTTP_OK,
    639       .requires_auth = true },
    640     { .url = "/monitoring/reserve-balance-insufficient-inconsistencies",
    641       .method = MHD_HTTP_METHOD_DELETE,
    642       .mime_type = "application/json",
    643       .data = NULL,
    644       .data_size = 0,
    645       .handler = &TAH_delete_generic,
    646       .response_code = MHD_HTTP_OK,
    647       .requires_auth = true,
    648       .table = TALER_AUDITORDB_RESERVE_BALANCE_INSUFFICIENT_INCONSISTENCY },
    649     { .url = "/monitoring/reserve-balance-insufficient-inconsistencies",
    650       .method = MHD_HTTP_METHOD_PATCH,
    651       .mime_type = "application/json",
    652       .data = NULL,
    653       .data_size = 0,
    654       .handler = &TAH_patch_generic_suppressed,
    655       .response_code = MHD_HTTP_OK,
    656       .requires_auth = true,
    657       .table = TALER_AUDITORDB_RESERVE_BALANCE_INSUFFICIENT_INCONSISTENCY },
    658     { .url = "/monitoring/purse-not-closed-inconsistencies",
    659       .method = MHD_HTTP_METHOD_GET,
    660       .mime_type = "application/json",
    661       .data = NULL,
    662       .data_size = 0,
    663       .handler = &TAH_get_monitoring_purse_not_closed_inconsistencies,
    664       .response_code = MHD_HTTP_OK,
    665       .requires_auth = true },
    666     { .url = "/monitoring/purse-not-closed-inconsistencies",
    667       .method = MHD_HTTP_METHOD_DELETE,
    668       .mime_type = "application/json",
    669       .data = NULL,
    670       .data_size = 0,
    671       .handler = &TAH_delete_generic,
    672       .response_code = MHD_HTTP_OK,
    673       .requires_auth = true,
    674       .table = TALER_AUDITORDB_PURSE_NOT_CLOSED_INCONSISTENCY },
    675     { .url = "/monitoring/purse-not-closed-inconsistencies",
    676       .method = MHD_HTTP_METHOD_PATCH,
    677       .mime_type = "application/json",
    678       .data = NULL,
    679       .data_size = 0,
    680       .handler = &TAH_patch_generic_suppressed,
    681       .response_code = MHD_HTTP_OK,
    682       .requires_auth = true,
    683       .table = TALER_AUDITORDB_PURSE_NOT_CLOSED_INCONSISTENCY  },
    684     { .url = "/monitoring/emergencies-by-count",
    685       .method = MHD_HTTP_METHOD_GET,
    686       .mime_type = "application/json",
    687       .data = NULL,
    688       .data_size = 0,
    689       .handler = &TAH_get_monitoring_emergency_by_count,
    690       .response_code = MHD_HTTP_OK,
    691       .requires_auth = true },
    692     { .url = "/monitoring/emergencies-by-count",
    693       .method = MHD_HTTP_METHOD_DELETE,
    694       .mime_type = "application/json",
    695       .data = NULL,
    696       .data_size = 0,
    697       .handler = &TAH_delete_generic,
    698       .response_code = MHD_HTTP_OK,
    699       .requires_auth = true,
    700       .table = TALER_AUDITORDB_EMERGENCY_BY_COUNT },
    701     { .url = "/monitoring/emergencies-by-count",
    702       .method = MHD_HTTP_METHOD_PATCH,
    703       .mime_type = "application/json",
    704       .data = NULL,
    705       .data_size = 0,
    706       .handler = &TAH_patch_generic_suppressed,
    707       .response_code = MHD_HTTP_OK,
    708       .requires_auth = true,
    709       .table = TALER_AUDITORDB_EMERGENCY_BY_COUNT },
    710     { .url = "/monitoring/reserve-in-inconsistencies",
    711       .method = MHD_HTTP_METHOD_GET,
    712       .mime_type = "application/json",
    713       .data = NULL,
    714       .data_size = 0,
    715       .handler = &TAH_get_monitoring_reserve_in_inconsistency,
    716       .response_code = MHD_HTTP_OK,
    717       .requires_auth = true },
    718     { .url = "/monitoring/reserve-in-inconsistencies",
    719       .method = MHD_HTTP_METHOD_DELETE,
    720       .mime_type = "application/json",
    721       .data = NULL,
    722       .data_size = 0,
    723       .handler = &TAH_delete_generic,
    724       .response_code = MHD_HTTP_OK,
    725       .requires_auth = true,
    726       .table = TALER_AUDITORDB_RESERVE_IN_INCONSISTENCY },
    727     { .url = "/monitoring/reserve-in-inconsistencies",
    728       .method = MHD_HTTP_METHOD_PATCH,
    729       .mime_type = "application/json",
    730       .data = NULL,
    731       .data_size = 0,
    732       .handler = &TAH_patch_generic_suppressed,
    733       .response_code = MHD_HTTP_OK,
    734       .requires_auth = true,
    735       .table = TALER_AUDITORDB_RESERVE_IN_INCONSISTENCY  },
    736     { .url = "/monitoring/kycauth-in-inconsistencies",
    737       .method = MHD_HTTP_METHOD_GET,
    738       .mime_type = "application/json",
    739       .data = NULL,
    740       .data_size = 0,
    741       .handler = &TAH_get_monitoring_kycauth_in_inconsistency,
    742       .response_code = MHD_HTTP_OK,
    743       .requires_auth = true },
    744     { .url = "/monitoring/kycauth-in-inconsistencies",
    745       .method = MHD_HTTP_METHOD_DELETE,
    746       .mime_type = "application/json",
    747       .data = NULL,
    748       .data_size = 0,
    749       .handler = &TAH_delete_generic,
    750       .response_code = MHD_HTTP_OK,
    751       .requires_auth = true,
    752       .table = TALER_AUDITORDB_KYCAUTH_IN_INCONSISTENCY },
    753     { .url = "/monitoring/kycauth-in-inconsistencies",
    754       .method = MHD_HTTP_METHOD_PATCH,
    755       .mime_type = "application/json",
    756       .data = NULL,
    757       .data_size = 0,
    758       .handler = &TAH_patch_generic_suppressed,
    759       .response_code = MHD_HTTP_OK,
    760       .requires_auth = true,
    761       .table = TALER_AUDITORDB_KYCAUTH_IN_INCONSISTENCY  },
    762     { .url = "/monitoring/reserve-not-closed-inconsistencies",
    763       .method = MHD_HTTP_METHOD_GET,
    764       .mime_type = "application/json",
    765       .data = NULL,
    766       .data_size = 0,
    767       .handler = &TAH_get_monitoring_reserve_not_closed_inconsistency,
    768       .response_code = MHD_HTTP_OK,
    769       .requires_auth = true },
    770     { .url = "/monitoring/reserve-not-closed-inconsistencies",
    771       .method = MHD_HTTP_METHOD_DELETE,
    772       .mime_type = "application/json",
    773       .data = NULL,
    774       .data_size = 0,
    775       .handler = &TAH_delete_generic,
    776       .response_code = MHD_HTTP_OK,
    777       .requires_auth = true,
    778       .table = TALER_AUDITORDB_RESERVE_NOT_CLOSED_INCONSISTENCY },
    779     { .url = "/monitoring/reserve-not-closed-inconsistencies",
    780       .method = MHD_HTTP_METHOD_PATCH,
    781       .mime_type = "application/json",
    782       .data = NULL,
    783       .data_size = 0,
    784       .handler = &TAH_patch_generic_suppressed,
    785       .response_code = MHD_HTTP_OK,
    786       .requires_auth = true,
    787       .table = TALER_AUDITORDB_RESERVE_NOT_CLOSED_INCONSISTENCY },
    788     { .url = "/monitoring/denominations-without-sigs",
    789       .method = MHD_HTTP_METHOD_GET,
    790       .mime_type = "application/json",
    791       .data = NULL,
    792       .data_size = 0,
    793       .handler = &TAH_get_monitoring_denominations_without_sigs,
    794       .response_code = MHD_HTTP_OK,
    795       .requires_auth = true },
    796     { .url = "/monitoring/denominations-without-sigs",
    797       .method = MHD_HTTP_METHOD_DELETE,
    798       .mime_type = "application/json",
    799       .data = NULL,
    800       .data_size = 0,
    801       .handler = &TAH_delete_generic,
    802       .response_code = MHD_HTTP_OK,
    803       .requires_auth = true,
    804       .table = TALER_AUDITORDB_DENOMINATIONS_WITHOUT_SIG },
    805     { .url = "/monitoring/denominations-without-sigs",
    806       .method = MHD_HTTP_METHOD_PATCH,
    807       .mime_type = "application/json",
    808       .data = NULL,
    809       .data_size = 0,
    810       .handler = &TAH_patch_generic_suppressed,
    811       .response_code = MHD_HTTP_OK,
    812       .requires_auth = true,
    813       .table = TALER_AUDITORDB_DENOMINATIONS_WITHOUT_SIG },
    814     { .url = "/monitoring/misattribution-in-inconsistencies",
    815       .method = MHD_HTTP_METHOD_GET,
    816       .mime_type = "application/json",
    817       .data = NULL,
    818       .data_size = 0,
    819       .handler = &TAH_get_monitoring_misattribution_in_inconsistency,
    820       .response_code = MHD_HTTP_OK,
    821       .requires_auth = true },
    822     { .url = "/monitoring/misattribution-in-inconsistencies",
    823       .method = MHD_HTTP_METHOD_DELETE,
    824       .mime_type = "application/json",
    825       .data = NULL,
    826       .data_size = 0,
    827       .handler = &TAH_delete_generic,
    828       .response_code = MHD_HTTP_OK,
    829       .requires_auth = true,
    830       .table = TALER_AUDITORDB_MISATTRIBUTION_IN_INCONSISTENCY },
    831     { .url = "/monitoring/misattribution-in-inconsistencies",
    832       .method = MHD_HTTP_METHOD_PATCH,
    833       .mime_type = "application/json",
    834       .data = NULL,
    835       .data_size = 0,
    836       .handler = &TAH_patch_generic_suppressed,
    837       .response_code = MHD_HTTP_OK,
    838       .requires_auth = true,
    839       .table = TALER_AUDITORDB_MISATTRIBUTION_IN_INCONSISTENCY },
    840     { .url = "/monitoring/reserves",
    841       .method = MHD_HTTP_METHOD_GET,
    842       .mime_type = "application/json",
    843       .data = NULL,
    844       .data_size = 0,
    845       .handler = &TAH_get_monitoring_reserves,
    846       .response_code = MHD_HTTP_OK,
    847       .requires_auth = true },
    848     { .url = "/monitoring/purses",
    849       .method = MHD_HTTP_METHOD_GET,
    850       .mime_type = "application/json",
    851       .data = NULL,
    852       .data_size = 0,
    853       .handler = &TAH_get_monitoring_purses,
    854       .response_code = MHD_HTTP_OK,
    855       .requires_auth = true },
    856     { .url = "/monitoring/historic-denomination-revenues",
    857       .method = MHD_HTTP_METHOD_GET,
    858       .mime_type = "application/json",
    859       .data = NULL,
    860       .data_size = 0,
    861       .handler = &TAH_get_monitoring_historic_denomination_revenue,
    862       .response_code = MHD_HTTP_OK,
    863       .requires_auth = true },
    864     { .url = "/monitoring/historic-reserve-summaries",
    865       .method = MHD_HTTP_METHOD_GET,
    866       .mime_type = "application/json",
    867       .data = NULL,
    868       .data_size = 0,
    869       .handler = &TAH_get_monitoring_historic_reserve_summary,
    870       .response_code = MHD_HTTP_OK,
    871       .requires_auth = true },
    872     { .url = "/monitoring/wire-format-inconsistencies",
    873       .method = MHD_HTTP_METHOD_GET,
    874       .mime_type = "application/json",
    875       .data = NULL,
    876       .data_size = 0,
    877       .handler = &TAH_get_monitoring_wire_format_inconsistency,
    878       .response_code = MHD_HTTP_OK,
    879       .requires_auth = true },
    880     { .url = "/monitoring/wire-format-inconsistencies",
    881       .method = MHD_HTTP_METHOD_DELETE,
    882       .mime_type = "application/json",
    883       .data = NULL,
    884       .data_size = 0,
    885       .handler = &TAH_delete_generic,
    886       .response_code = MHD_HTTP_OK,
    887       .requires_auth = true,
    888       .table = TALER_AUDITORDB_WIRE_FORMAT_INCONSISTENCY },
    889     { .url = "/monitoring/wire-format-inconsistencies",
    890       .method = MHD_HTTP_METHOD_PATCH,
    891       .mime_type = "application/json",
    892       .data = NULL,
    893       .data_size = 0,
    894       .handler = &TAH_patch_generic_suppressed,
    895       .response_code = MHD_HTTP_OK,
    896       .requires_auth = true,
    897       .table = TALER_AUDITORDB_WIRE_FORMAT_INCONSISTENCY },
    898     { .url = "/monitoring/wire-out-inconsistencies",
    899       .method = MHD_HTTP_METHOD_GET,
    900       .mime_type = "application/json",
    901       .data = NULL,
    902       .data_size = 0,
    903       .handler = &TAH_get_monitoring_wire_out_inconsistency,
    904       .response_code = MHD_HTTP_OK,
    905       .requires_auth = true },
    906     { .url = "/monitoring/wire-out-inconsistencies",
    907       .method = MHD_HTTP_METHOD_DELETE,
    908       .mime_type = "application/json",
    909       .data = NULL,
    910       .data_size = 0,
    911       .handler = &TAH_delete_generic,
    912       .response_code = MHD_HTTP_OK,
    913       .requires_auth = true,
    914       .table = TALER_AUDITORDB_WIRE_OUT_INCONSISTENCY },
    915     { .url = "/monitoring/wire-out-inconsistencies",
    916       .method = MHD_HTTP_METHOD_PATCH,
    917       .mime_type = "application/json",
    918       .data = NULL,
    919       .data_size = 0,
    920       .handler = &TAH_patch_generic_suppressed,
    921       .response_code = MHD_HTTP_OK,
    922       .requires_auth = true,
    923       .table = TALER_AUDITORDB_WIRE_OUT_INCONSISTENCY },
    924     { .url = "/monitoring/reserve-balance-summary-wrong-inconsistencies",
    925       .method = MHD_HTTP_METHOD_GET,
    926       .mime_type = "application/json",
    927       .data = NULL,
    928       .data_size = 0,
    929       .handler = &TAH_get_monitoring_reserve_balance_summary_wrong_inconsistency
    930       ,
    931       .response_code = MHD_HTTP_OK,
    932       .requires_auth = true },
    933     { .url = "/monitoring/reserve-balance-summary-wrong-inconsistencies",
    934       .method = MHD_HTTP_METHOD_DELETE,
    935       .mime_type = "application/json",
    936       .data = NULL,
    937       .data_size = 0,
    938       .handler = &TAH_delete_generic,
    939       .response_code = MHD_HTTP_OK,
    940       .requires_auth = true,
    941       .table = TALER_AUDITORDB_RESERVE_BALANCE_SUMMARY_WRONG_INCONSISTENCY },
    942     { .url = "/monitoring/reserve-balance-summary-wrong-inconsistencies",
    943       .method = MHD_HTTP_METHOD_PATCH,
    944       .mime_type = "application/json",
    945       .data = NULL,
    946       .data_size = 0,
    947       .handler = &TAH_patch_generic_suppressed,
    948       .response_code = MHD_HTTP_OK,
    949       .requires_auth = true,
    950       .table = TALER_AUDITORDB_RESERVE_BALANCE_SUMMARY_WRONG_INCONSISTENCY },
    951     { .url = "/monitoring/row-minor-inconsistencies",
    952       .method = MHD_HTTP_METHOD_GET,
    953       .mime_type = "application/json",
    954       .data = NULL,
    955       .data_size = 0,
    956       .handler = &TAH_get_monitoring_row_minor_inconsistencies,
    957       .response_code = MHD_HTTP_OK,
    958       .requires_auth = true },
    959     { .url = "/monitoring/row-minor-inconsistencies",
    960       .method = MHD_HTTP_METHOD_DELETE,
    961       .mime_type = "application/json",
    962       .data = NULL,
    963       .data_size = 0,
    964       .handler = &TAH_delete_generic,
    965       .response_code = MHD_HTTP_OK,
    966       .requires_auth = true,
    967       .table = TALER_AUDITORDB_ROW_MINOR_INCONSISTENCY },
    968     { .url = "/monitoring/row-minor-inconsistencies",
    969       .method = MHD_HTTP_METHOD_PATCH,
    970       .mime_type = "application/json",
    971       .data = NULL,
    972       .data_size = 0,
    973       .handler = &TAH_patch_generic_suppressed,
    974       .response_code = MHD_HTTP_OK,
    975       .requires_auth = true,
    976       .table = TALER_AUDITORDB_ROW_MINOR_INCONSISTENCY },
    977     { .url = "/monitoring/fee-time-inconsistencies",
    978       .method = MHD_HTTP_METHOD_GET,
    979       .mime_type = "application/json",
    980       .data = NULL,
    981       .data_size = 0,
    982       .handler = &TAH_get_monitoring_fee_time_inconsistency,
    983       .response_code = MHD_HTTP_OK,
    984       .requires_auth = true },
    985     { .url = "/monitoring/fee-time-inconsistencies",
    986       .method = MHD_HTTP_METHOD_DELETE,
    987       .mime_type = "application/json",
    988       .data = NULL,
    989       .data_size = 0,
    990       .handler = &TAH_delete_generic,
    991       .response_code = MHD_HTTP_OK,
    992       .requires_auth = true,
    993       .table =  TALER_AUDITORDB_FEE_TIME_INCONSISTENCY },
    994     { .url = "/monitoring/fee-time-inconsistencies",
    995       .method = MHD_HTTP_METHOD_PATCH,
    996       .mime_type = "application/json",
    997       .data = NULL,
    998       .data_size = 0,
    999       .handler = &TAH_patch_generic_suppressed,
   1000       .response_code = MHD_HTTP_OK,
   1001       .requires_auth = true,
   1002       .table =  TALER_AUDITORDB_FEE_TIME_INCONSISTENCY  },
   1003     { .url = "/monitoring/balances",
   1004       .method = MHD_HTTP_METHOD_GET,
   1005       .mime_type = "application/json",
   1006       .data = NULL,
   1007       .data_size = 0,
   1008       .handler = &TAH_get_monitoring_balances,
   1009       .response_code = MHD_HTTP_OK,
   1010       .requires_auth = true },
   1011     { .url = "/monitoring/progress",
   1012       .method = MHD_HTTP_METHOD_GET,
   1013       .mime_type = "application/json",
   1014       .data = NULL,
   1015       .data_size = 0,
   1016       .handler = &TAH_get_monitoring_progress,
   1017       .response_code = MHD_HTTP_OK,
   1018       .requires_auth = true },
   1019     { .url = "/config",
   1020       .method = MHD_HTTP_METHOD_GET,
   1021       .mime_type = "application/json",
   1022       .data = NULL,
   1023       .data_size = 0,
   1024       .handler = &handle_config,
   1025       .response_code = MHD_HTTP_OK,
   1026       .requires_auth = false },
   1027     /* /robots.txt: disallow everything */
   1028     { .url = "/robots.txt",
   1029       .method = MHD_HTTP_METHOD_GET,
   1030       .mime_type = "text/plain",
   1031       .data = "User-agent: *\nDisallow: /\n",
   1032       .data_size = 0,
   1033       .handler = &TAH_MHD_handler_static_response,
   1034       .response_code = MHD_HTTP_OK,
   1035       .requires_auth = false },
   1036     /* AGPL licensing page, redirect to source. As per the AGPL-license,
   1037        every deployment is required to offer the user a download of the
   1038        source. We make this easy by including a redirect t the source
   1039        here. */
   1040     { .url = "/agpl",
   1041       .method = MHD_HTTP_METHOD_GET,
   1042       .mime_type = "text/plain",
   1043       .data = NULL,
   1044       .data_size = 0,
   1045       .handler = &TAH_MHD_handler_agpl_redirect,
   1046       .response_code = MHD_HTTP_FOUND,
   1047       .requires_auth = false },
   1048     /* Landing page, for now tells humans to go away
   1049      * (NOTE: ideally, the reverse proxy will respond with a nicer page) */
   1050     { .url = "/",
   1051       .method = MHD_HTTP_METHOD_GET,
   1052       .mime_type = "text/plain",
   1053       .data =
   1054         "Hello, I'm the Taler auditor. This HTTP server is not for humans.\n",
   1055       .data_size = 0,
   1056       .handler = &TAH_MHD_handler_static_response,
   1057       .response_code = MHD_HTTP_OK,
   1058       .requires_auth = false },
   1059     { NULL, NULL, NULL, NULL, 0, NULL, 0, 0 }
   1060   };
   1061   unsigned int args_max = 3;
   1062   const char *args[args_max + 1];
   1063   size_t ulen = strlen (url) + 1;
   1064   char d[ulen];
   1065   /* const */ struct TAH_RequestHandler *match = NULL;
   1066   bool url_match = false;
   1067 
   1068   (void) cls;
   1069   (void) version;
   1070   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1071               "Handling request for URL '%s'\n",
   1072               url);
   1073   if (0 == strcasecmp (method,
   1074                        MHD_HTTP_METHOD_HEAD))
   1075     method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
   1076   if (0 == strcasecmp (method,
   1077                        MHD_HTTP_METHOD_OPTIONS) )
   1078     return TALER_MHD_reply_cors_preflight (connection);
   1079 
   1080   memset (&args,
   1081           0,
   1082           sizeof (args));
   1083   GNUNET_memcpy (d,
   1084                  url,
   1085                  ulen);
   1086   {
   1087     unsigned int i = 0;
   1088 
   1089     for (args[i] = strtok (d,
   1090                            "/");
   1091          NULL != args[i];
   1092          args[i] = strtok (NULL,
   1093                            "/"))
   1094     {
   1095       i++;
   1096       if (i > args_max)
   1097       {
   1098         /* more than @e args_max segments in the URL */
   1099         GNUNET_break_op (0);
   1100         goto not_found;
   1101       }
   1102     }
   1103   }
   1104 
   1105   for (unsigned int i = 0; NULL != handlers[i].url; i++)
   1106   {
   1107     /* const */ struct TAH_RequestHandler *rh = &handlers[i];
   1108 
   1109     if ( (0 == strcmp (url,
   1110                        rh->url)) ||
   1111          ( (0 == strncmp (url,
   1112                           rh->url,
   1113                           strlen (rh->url))) &&
   1114            ('/' == url[strlen (rh->url)]) ) )
   1115     {
   1116       url_match = true;
   1117       if ( (NULL == rh->method) ||
   1118            (0 == strcasecmp (method,
   1119                              rh->method)) )
   1120       {
   1121         match = rh;
   1122         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1123                     "Matched %s\n",
   1124                     rh->url);
   1125         break;
   1126       }
   1127     }
   1128   }
   1129   if (NULL == match)
   1130   {
   1131     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1132                 "Could not find handler for `%s'\n",
   1133                 url);
   1134     goto not_found;
   1135   }
   1136   if (match->requires_auth &&
   1137       (0 == disable_auth) )
   1138   {
   1139     const char *auth;
   1140 
   1141     auth = MHD_lookup_connection_value (connection,
   1142                                         MHD_HEADER_KIND,
   1143                                         MHD_HTTP_HEADER_AUTHORIZATION);
   1144     if (NULL == auth)
   1145     {
   1146       GNUNET_break_op (0);
   1147       return TALER_MHD_reply_with_error (
   1148         connection,
   1149         MHD_HTTP_UNAUTHORIZED,
   1150         TALER_EC_AUDITOR_GENERIC_UNAUTHORIZED,
   1151         "Check 'Authorization' header");
   1152     }
   1153     extract_token (&auth);
   1154     if (NULL == auth)
   1155       return TALER_MHD_reply_with_error (
   1156         connection,
   1157         MHD_HTTP_BAD_REQUEST,
   1158         TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1159         "'" RFC_8959_PREFIX
   1160         "' prefix or 'Bearer' missing in 'Authorization' header");
   1161 
   1162     if (GNUNET_OK !=
   1163         check_auth (auth))
   1164     {
   1165       GNUNET_break_op (0);
   1166       return TALER_MHD_reply_with_error (
   1167         connection,
   1168         MHD_HTTP_UNAUTHORIZED,
   1169         TALER_EC_AUDITOR_GENERIC_UNAUTHORIZED,
   1170         "Check 'Authorization' header");
   1171     }
   1172   }
   1173 
   1174   return match->handler (match,
   1175                          connection,
   1176                          con_cls,
   1177                          upload_data,
   1178                          upload_data_size,
   1179                          args);
   1180 not_found:
   1181   if (url_match)
   1182   {
   1183     /* The URL exists, but not for the requested HTTP method: respond with
   1184        405 Method Not Allowed and an 'Allow' header listing the methods that
   1185        are supported for this URL (#9424). */
   1186     char allow[128] = "OPTIONS";
   1187     size_t aoff = strlen ("OPTIONS");
   1188     struct MHD_Response *resp;
   1189     enum MHD_Result ret;
   1190 
   1191     GNUNET_break_op (0);
   1192     /* OPTIONS is always supported (handled above); additionally list every
   1193        method registered for this URL. */
   1194     for (unsigned int i = 0; NULL != handlers[i].url; i++)
   1195     {
   1196       const struct TAH_RequestHandler *rh = &handlers[i];
   1197 
   1198       if (NULL == rh->method)
   1199         continue;
   1200       if ( (0 != strcmp (url,
   1201                          rh->url)) &&
   1202            ! ( (0 == strncmp (url,
   1203                               rh->url,
   1204                               strlen (rh->url))) &&
   1205                ('/' == url[strlen (rh->url)]) ) )
   1206         continue;
   1207       GNUNET_assert (aoff + strlen (rh->method) + 3 < sizeof (allow));
   1208       memcpy (&allow[aoff],
   1209               ", ",
   1210               2);
   1211       aoff += 2;
   1212       memcpy (&allow[aoff],
   1213               rh->method,
   1214               strlen (rh->method));
   1215       aoff += strlen (rh->method);
   1216       allow[aoff] = '\0';
   1217     }
   1218     resp = MHD_create_response_from_buffer (0,
   1219                                             NULL,
   1220                                             MHD_RESPMEM_PERSISTENT);
   1221     TALER_MHD_add_global_headers (resp,
   1222                                   false);
   1223     GNUNET_break (MHD_YES ==
   1224                   MHD_add_response_header (resp,
   1225                                            MHD_HTTP_HEADER_ALLOW,
   1226                                            allow));
   1227     ret = MHD_queue_response (connection,
   1228                               MHD_HTTP_METHOD_NOT_ALLOWED,
   1229                               resp);
   1230     MHD_destroy_response (resp);
   1231     return ret;
   1232   }
   1233 
   1234 #define NOT_FOUND \
   1235         "<html><title>404: not found</title><body>auditor endpoints have been moved to /monitoring/...</body></html>"
   1236   return TALER_MHD_reply_static (connection,
   1237                                  MHD_HTTP_NOT_FOUND,
   1238                                  "text/html",
   1239                                  NOT_FOUND,
   1240                                  strlen (NOT_FOUND));
   1241 #undef NOT_FOUND
   1242 }
   1243 
   1244 
   1245 /**
   1246  * Load configuration parameters for the auditor
   1247  * server into the corresponding global variables.
   1248  *
   1249  * @return EXIT_SUCCESS on success, EXIT_NOTCONFIGURED for invalid settings,
   1250  *         EXIT_FAILURE for an unavailable dependency
   1251  */
   1252 static int
   1253 auditor_serve_process_config (void)
   1254 {
   1255   bool have_auditor_public_key = false;
   1256 
   1257   if (NULL ==
   1258       (TAH_apg = TALER_AUDITORDB_connect (cfg)))
   1259   {
   1260     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1261                 "Failed to initialize DB subsystem to interact with auditor database\n");
   1262     return EXIT_FAILURE;
   1263   }
   1264   if (NULL ==
   1265       (TAH_epg = TALER_EXCHANGEDB_connect (cfg)))
   1266   {
   1267     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1268                 "Failed to initialize DB subsystem to query exchange database\n");
   1269     return EXIT_FAILURE;
   1270   }
   1271   if (GNUNET_SYSERR ==
   1272       TALER_EXCHANGEDB_preflight (TAH_epg))
   1273   {
   1274     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1275                 "Failed to initialize DB subsystem to query exchange database\n");
   1276     return EXIT_FAILURE;
   1277   }
   1278   if (GNUNET_OK !=
   1279       TALER_config_get_currency (cfg,
   1280                                  "exchange",
   1281                                  &TAH_currency))
   1282   {
   1283     return EXIT_NOTCONFIGURED;
   1284   }
   1285 
   1286   {
   1287     char *master_public_key_str;
   1288 
   1289     if (GNUNET_OK !=
   1290         GNUNET_CONFIGURATION_get_value_string (cfg,
   1291                                                "exchange",
   1292                                                "MASTER_PUBLIC_KEY",
   1293                                                &master_public_key_str))
   1294     {
   1295       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1296                                  "exchange",
   1297                                  "MASTER_PUBLIC_KEY");
   1298       return EXIT_NOTCONFIGURED;
   1299     }
   1300     if (GNUNET_OK !=
   1301         GNUNET_CRYPTO_eddsa_public_key_from_string (
   1302           master_public_key_str,
   1303           strlen (master_public_key_str),
   1304           &TAH_master_public_key.eddsa_pub))
   1305     {
   1306       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1307                                  "exchange",
   1308                                  "MASTER_PUBLIC_KEY",
   1309                                  "invalid base32 encoding for a master public key");
   1310       GNUNET_free (master_public_key_str);
   1311       return EXIT_NOTCONFIGURED;
   1312     }
   1313     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1314                 "Launching auditor for exchange `%s'...\n",
   1315                 master_public_key_str);
   1316     GNUNET_free (master_public_key_str);
   1317   }
   1318 
   1319   {
   1320     char *pub;
   1321 
   1322     if (GNUNET_OK ==
   1323         GNUNET_CONFIGURATION_get_value_string (cfg,
   1324                                                "AUDITOR",
   1325                                                "PUBLIC_KEY",
   1326                                                &pub))
   1327     {
   1328       if (GNUNET_OK !=
   1329           GNUNET_CRYPTO_eddsa_public_key_from_string (pub,
   1330                                                       strlen (pub),
   1331                                                       &auditor_pub.eddsa_pub))
   1332       {
   1333         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1334                     "Invalid public key given in auditor configuration.");
   1335         GNUNET_free (pub);
   1336         return EXIT_NOTCONFIGURED;
   1337       }
   1338       GNUNET_free (pub);
   1339       have_auditor_public_key = true;
   1340     }
   1341   }
   1342 
   1343   if (! have_auditor_public_key)
   1344   {
   1345     /* Fall back to trying to read private key */
   1346     char *auditor_key_file;
   1347     struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
   1348 
   1349     if (GNUNET_OK !=
   1350         GNUNET_CONFIGURATION_get_value_filename (cfg,
   1351                                                  "auditor",
   1352                                                  "AUDITOR_PRIV_FILE",
   1353                                                  &auditor_key_file))
   1354     {
   1355       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1356                                  "AUDITOR",
   1357                                  "PUBLIC_KEY");
   1358       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1359                                  "AUDITOR",
   1360                                  "AUDITOR_PRIV_FILE");
   1361       return EXIT_NOTCONFIGURED;
   1362     }
   1363     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1364                 "Loading auditor private key from %s\n",
   1365                 auditor_key_file);
   1366     if (GNUNET_OK !=
   1367         GNUNET_CRYPTO_eddsa_key_from_file (auditor_key_file,
   1368                                            GNUNET_NO,
   1369                                            &eddsa_priv))
   1370     {
   1371       /* Both failed, complain! */
   1372       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1373                                  "AUDITOR",
   1374                                  "PUBLIC_KEY");
   1375       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1376                   "Failed to initialize auditor key from file `%s'\n",
   1377                   auditor_key_file);
   1378       GNUNET_free (auditor_key_file);
   1379       return 1;
   1380     }
   1381     GNUNET_free (auditor_key_file);
   1382     GNUNET_CRYPTO_eddsa_key_get_public (&eddsa_priv,
   1383                                         &auditor_pub.eddsa_pub);
   1384   }
   1385   if (GNUNET_OK ==
   1386       GNUNET_CONFIGURATION_get_value_filename (cfg,
   1387                                                "auditor",
   1388                                                "SPA_DIR",
   1389                                                &TAH_spa_dir))
   1390   {
   1391     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1392                 "Loading auditor SPA from %s\n",
   1393                 TAH_spa_dir);
   1394   }
   1395   else
   1396   {
   1397     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1398                 "Loading auditor SPA from default location\n");
   1399   }
   1400   return EXIT_SUCCESS;
   1401 }
   1402 
   1403 
   1404 /**
   1405  * Function run on shutdown.
   1406  *
   1407  * @param cls NULL
   1408  */
   1409 static void
   1410 do_shutdown (void *cls)
   1411 {
   1412   (void) cls;
   1413   TALER_MHD_daemons_halt ();
   1414   TEAH_put_deposit_confirmation_done ();
   1415   TALER_MHD_daemons_destroy ();
   1416   if (NULL != TAH_apg)
   1417   {
   1418     TALER_AUDITORDB_disconnect (TAH_apg);
   1419     TAH_apg = NULL;
   1420   }
   1421   if (NULL != TAH_epg)
   1422   {
   1423     TALER_EXCHANGEDB_disconnect (TAH_epg);
   1424     TAH_epg = NULL;
   1425   }
   1426 }
   1427 
   1428 
   1429 /**
   1430  * Callback invoked on every listen socket to start the
   1431  * respective MHD HTTP daemon.
   1432  *
   1433  * @param cls unused
   1434  * @param lsock the listen socket
   1435  */
   1436 static void
   1437 start_daemon (void *cls,
   1438               int lsock)
   1439 {
   1440   struct MHD_Daemon *mhd;
   1441 
   1442   (void) cls;
   1443   GNUNET_assert (-1 != lsock);
   1444   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
   1445                           | MHD_USE_PIPE_FOR_SHUTDOWN
   1446                           | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
   1447                           | MHD_USE_TCP_FASTOPEN,
   1448                           0,
   1449                           NULL, NULL,
   1450                           &handle_mhd_request, NULL,
   1451                           MHD_OPTION_LISTEN_SOCKET,
   1452                           lsock,
   1453                           MHD_OPTION_EXTERNAL_LOGGER,
   1454                           &TALER_MHD_handle_logs,
   1455                           NULL,
   1456                           MHD_OPTION_NOTIFY_COMPLETED,
   1457                           &handle_mhd_completion_callback,
   1458                           NULL,
   1459                           MHD_OPTION_CONNECTION_TIMEOUT,
   1460                           connection_timeout,
   1461                           MHD_OPTION_END);
   1462   if (NULL == mhd)
   1463   {
   1464     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1465                 "Failed to launch HTTP daemon.\n");
   1466     GNUNET_SCHEDULER_shutdown ();
   1467     return;
   1468   }
   1469   have_daemons = true;
   1470   TALER_MHD_daemon_start (mhd);
   1471 }
   1472 
   1473 
   1474 /**
   1475  * Main function that will be run by the scheduler.
   1476  *
   1477  * @param cls closure
   1478  * @param args remaining command-line arguments
   1479  * @param cfgfile name of the configuration file used (for saving, can be
   1480  *        NULL!)
   1481  * @param config configuration
   1482  */
   1483 static void
   1484 run (void *cls,
   1485      char *const *args,
   1486      const char *cfgfile,
   1487      const struct GNUNET_CONFIGURATION_Handle *config)
   1488 {
   1489   enum TALER_MHD_GlobalOptions go;
   1490   enum GNUNET_GenericReturnValue ret;
   1491 
   1492   (void) cls;
   1493   (void) args;
   1494   (void) cfgfile;
   1495   if (0 == disable_auth)
   1496   {
   1497     const char *tok;
   1498 
   1499     tok = getenv ("TALER_AUDITOR_ACCESS_TOKEN");
   1500     if (NULL == tok)
   1501     {
   1502       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1503                   "TALER_AUDITOR_ACCESS_TOKEN environment variable not set. Disabling authentication\n");
   1504       disable_auth = 1;
   1505     }
   1506     else
   1507     {
   1508       GNUNET_assert (GNUNET_YES ==
   1509                      GNUNET_CRYPTO_hkdf_gnunet (
   1510                        &TAH_auth,
   1511                        sizeof (TAH_auth),
   1512                        KDF_SALT,
   1513                        strlen (KDF_SALT),
   1514                        tok,
   1515                        strlen (tok)));
   1516     }
   1517   }
   1518 
   1519   go = TALER_MHD_GO_NONE;
   1520   if (auditor_connection_close)
   1521     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
   1522   TALER_MHD_setup (go);
   1523   cfg = config;
   1524 
   1525   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   1526                                  NULL);
   1527   if (EXIT_SUCCESS !=
   1528       (global_ret = auditor_serve_process_config ()))
   1529   {
   1530     GNUNET_SCHEDULER_shutdown ();
   1531     return;
   1532   }
   1533   if (GNUNET_OK !=
   1534       TAH_spa_init ())
   1535   {
   1536     global_ret = EXIT_NOTCONFIGURED;
   1537     GNUNET_SCHEDULER_shutdown ();
   1538     return;
   1539   }
   1540   TEAH_put_deposit_confirmation_init ();
   1541   ret = TALER_MHD_listen_bind (cfg,
   1542                                "auditor",
   1543                                &start_daemon,
   1544                                NULL);
   1545   switch (ret)
   1546   {
   1547   case GNUNET_SYSERR:
   1548     global_ret = EXIT_NOTCONFIGURED;
   1549     GNUNET_SCHEDULER_shutdown ();
   1550     return;
   1551   case GNUNET_NO:
   1552     if (! have_daemons)
   1553     {
   1554       global_ret = EXIT_FAILURE;
   1555       GNUNET_SCHEDULER_shutdown ();
   1556       return;
   1557     }
   1558     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1559                 "Could not open all configured listen sockets\n");
   1560     break;
   1561   case GNUNET_OK:
   1562     break;
   1563   }
   1564   global_ret = EXIT_SUCCESS;
   1565 }
   1566 
   1567 
   1568 /**
   1569  * The main function of the taler-auditor-httpd server ("the auditor").
   1570  *
   1571  * @param argc number of arguments from the command line
   1572  * @param argv command line arguments
   1573  * @return 0 ok, 1 on error
   1574  */
   1575 int
   1576 main (int argc,
   1577       char *const *argv)
   1578 {
   1579   const struct GNUNET_GETOPT_CommandLineOption options[] = {
   1580     GNUNET_GETOPT_option_flag ('C',
   1581                                "connection-close",
   1582                                "force HTTP connections to be closed after each request",
   1583                                &auditor_connection_close),
   1584     GNUNET_GETOPT_option_flag ('n',
   1585                                "no-authentication",
   1586                                "disable authentication checks",
   1587                                &disable_auth),
   1588     GNUNET_GETOPT_option_uint ('t',
   1589                                "timeout",
   1590                                "SECONDS",
   1591                                "after how long do connections timeout by default (in seconds)",
   1592                                &connection_timeout),
   1593     GNUNET_GETOPT_option_help (
   1594       TALER_AUDITOR_project_data (),
   1595       "HTTP server providing a RESTful API to access a Taler auditor"),
   1596     GNUNET_GETOPT_option_version (VERSION),
   1597     GNUNET_GETOPT_OPTION_END
   1598   };
   1599   int ret;
   1600 
   1601   ret = GNUNET_PROGRAM_run (
   1602     TALER_AUDITOR_project_data (),
   1603     argc, argv,
   1604     "taler-auditor-httpd",
   1605     "Taler auditor HTTP service",
   1606     options,
   1607     &run, NULL);
   1608   if (GNUNET_SYSERR == ret)
   1609     return EXIT_NOTCONFIGURED;
   1610   if (GNUNET_NO == ret)
   1611     return EXIT_SUCCESS;
   1612   return global_ret;
   1613 }
   1614 
   1615 
   1616 /* end of taler-auditor-httpd.c */