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


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