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 (49443B)


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