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


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