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


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