commit 5dbf9d7cf1b79cdbcb09b96d5745437b9ef4aea9
parent a467cb23e3d5067a0e4aac6f9022af3fd883325c
Author: Christian Grothoff <christian@grothoff.org>
Date: Sat, 4 Apr 2026 13:26:57 +0200
change /reserves-attest/RPUB to /reserves/RPUB/attest for consistency
Diffstat:
20 files changed, 1542 insertions(+), 1493 deletions(-)
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
@@ -197,10 +197,10 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_post-recoup-withdraw.c taler-exchange-httpd_post-recoup-withdraw.h \
taler-exchange-httpd_post-recoup-refresh.c taler-exchange-httpd_post-recoup-refresh.h \
taler-exchange-httpd_post-coins-COIN_PUB-refund.c taler-exchange-httpd_post-coins-COIN_PUB-refund.h \
- taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.c taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.h \
+ taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.c taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.h \
taler-exchange-httpd_post-reserves-RESERVE_PUB-close.c taler-exchange-httpd_post-reserves-RESERVE_PUB-close.h \
taler-exchange-httpd_get-reserves-RESERVE_PUB.c taler-exchange-httpd_get-reserves-RESERVE_PUB.h \
- taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.c taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.h \
+ taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.c taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.h \
taler-exchange-httpd_get-reserves-RESERVE_PUB-history.c taler-exchange-httpd_get-reserves-RESERVE_PUB-history.h \
taler-exchange-httpd_post-reserves-RESERVE_PUB-open.c taler-exchange-httpd_post-reserves-RESERVE_PUB-open.h \
taler-exchange-httpd_post-reserves-RESERVE_PUB-purse.c taler-exchange-httpd_post-reserves-RESERVE_PUB-purse.h \
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
@@ -69,10 +69,10 @@
#include "taler-exchange-httpd_post-recoup-withdraw.h"
#include "taler-exchange-httpd_post-recoup-refresh.h"
#include "taler-exchange-httpd_post-coins-COIN_PUB-refund.h"
-#include "taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.h"
+#include "taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.h"
#include "taler-exchange-httpd_post-reserves-RESERVE_PUB-close.h"
#include "taler-exchange-httpd_get-reserves-RESERVE_PUB.h"
-#include "taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.h"
+#include "taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.h"
#include "taler-exchange-httpd_get-reserves-RESERVE_PUB-history.h"
#include "taler-exchange-httpd_post-reserves-RESERVE_PUB-open.h"
#include "taler-exchange-httpd_post-reserves-RESERVE_PUB-purse.h"
@@ -657,15 +657,23 @@ handle_get_aml (struct TEH_RequestContext *rc,
GNUNET_STRINGS_string_to_data (sig_hdr,
strlen (sig_hdr),
&officer_sig,
- sizeof (officer_sig))) ||
- (GNUNET_OK !=
- TALER_officer_aml_query_verify (&officer_pub,
- &officer_sig)) )
+ sizeof (officer_sig))) )
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (
rc->connection,
MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
+ TALER_AML_OFFICER_SIGNATURE_HEADER);
+ }
+ if (GNUNET_OK !=
+ TALER_officer_aml_query_verify (&officer_pub,
+ &officer_sig))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID,
sig_hdr);
}
@@ -763,6 +771,10 @@ handle_post_reserves (struct TEH_RequestContext *rc,
.handler = &TEH_handler_reserves_close
},
{
+ .op = "attest",
+ .handler = &TEH_handler_reserves_attest
+ },
+ {
.op = NULL,
.handler = NULL
},
@@ -838,6 +850,10 @@ handle_get_reserves (struct TEH_RequestContext *rc,
.handler = &TEH_handler_reserves_history
},
{
+ .op = "attest",
+ .handler = &TEH_handler_get_reserves_attest
+ },
+ {
.op = NULL,
.handler = NULL
},
@@ -1691,18 +1707,6 @@ handle_mhd_request (void *cls,
.handler.post = &TEH_handler_reveal_melt,
.nargs = 0
},
- {
- .url = "reserves-attest",
- .method = MHD_HTTP_METHOD_GET,
- .handler.get = &TEH_handler_reserves_get_attest,
- .nargs = 1
- },
- {
- .url = "reserves-attest",
- .method = MHD_HTTP_METHOD_POST,
- .handler.post = &TEH_handler_reserves_attest,
- .nargs = 1
- },
/* coins */
{
.url = "coins",
diff --git a/src/exchange/taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.c b/src/exchange/taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.c
@@ -0,0 +1,177 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.c
+ * @brief Handle GET /reserves/$RESERVE_PUB/attest requests
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler/taler_kyclogic_lib.h"
+#include "taler/taler_mhd_lib.h"
+#include "taler/taler_json_lib.h"
+#include "taler/taler_dbevents.h"
+#include "taler-exchange-httpd_get-keys.h"
+#include "taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Closure for #reserve_attest_transaction.
+ */
+struct ReserveAttestContext
+{
+
+ /**
+ * Hash of the payto URI of this reserve.
+ */
+ struct TALER_NormalizedPaytoHashP h_payto;
+
+ /**
+ * Available attributes.
+ */
+ json_t *attributes;
+
+};
+
+
+/**
+ * Function called with information about all applicable
+ * legitimization processes for the given user.
+ *
+ * @param cls our `struct ReserveAttestContext *`
+ * @param h_payto account for which the attribute data is stored
+ * @param provider_name provider that must be checked
+ * @param collection_time when was the data collected
+ * @param expiration_time when does the data expire
+ * @param enc_attributes_size number of bytes in @a enc_attributes
+ * @param enc_attributes encrypted attribute data
+ */
+static void
+kyc_process_cb (void *cls,
+ const struct TALER_NormalizedPaytoHashP *h_payto,
+ const char *provider_name,
+ struct GNUNET_TIME_Timestamp collection_time,
+ struct GNUNET_TIME_Timestamp expiration_time,
+ size_t enc_attributes_size,
+ const void *enc_attributes)
+{
+ struct ReserveAttestContext *rsc = cls;
+ json_t *attrs;
+ json_t *val;
+ const char *name;
+
+ if (GNUNET_TIME_absolute_is_past (
+ expiration_time.abs_time))
+ return;
+ attrs = TALER_CRYPTO_kyc_attributes_decrypt (
+ &TEH_attribute_key,
+ enc_attributes,
+ enc_attributes_size);
+ json_object_foreach (attrs, name, val)
+ {
+ bool duplicate = false;
+ size_t idx;
+ json_t *str;
+
+ json_array_foreach (rsc->attributes, idx, str)
+ {
+ if (0 == strcmp (json_string_value (str),
+ name))
+ {
+ duplicate = true;
+ break;
+ }
+ }
+ if (duplicate)
+ continue;
+ GNUNET_assert (0 ==
+ json_array_append_new (rsc->attributes,
+ json_string (name)));
+ }
+ json_decref (attrs);
+}
+
+
+MHD_RESULT
+TEH_handler_get_reserves_attest (
+ struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub)
+{
+ struct ReserveAttestContext rsc = {
+ .attributes = NULL
+ };
+
+ {
+ struct TALER_NormalizedPayto payto_uri;
+
+ payto_uri
+ = TALER_reserve_make_payto (TEH_base_url,
+ reserve_pub);
+ TALER_normalized_payto_hash (payto_uri,
+ &rsc.h_payto);
+ GNUNET_free (payto_uri.normalized_payto);
+ }
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ rsc.attributes = json_array ();
+ GNUNET_assert (NULL != rsc.attributes);
+ qs = TEH_plugin->select_kyc_attributes (TEH_plugin->cls,
+ &rsc.h_payto,
+ &kyc_process_cb,
+ &rsc);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ json_decref (rsc.attributes);
+ rsc.attributes = NULL;
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "select_kyc_attributes");
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ json_decref (rsc.attributes);
+ rsc.attributes = NULL;
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "select_kyc_attributes");
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break_op (0);
+ json_decref (rsc.attributes);
+ rsc.attributes = NULL;
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
+ NULL);
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ }
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_array_steal ("details",
+ rsc.attributes));
+}
+
+
+/* end of taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.c */
diff --git a/src/exchange/taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.h b/src/exchange/taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.h
@@ -0,0 +1,46 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2020 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.h
+ * @brief Handle /reserves/$RESERVE_PUB GET_ATTEST requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_GET_RESERVES_RESERVE_PUB_ATTEST_H
+#define TALER_EXCHANGE_HTTPD_GET_RESERVES_RESERVE_PUB_ATTEST_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a GET "/reserves/$RID/attest" request. Parses the
+ * given "reserve_pub" in @a args (which should contain the
+ * EdDSA public key of a reserve) and then responds with the
+ * available attestations for the reserve.
+ *
+ * @param rc request context
+ * @param reserve_pub public key of the reserve to get available
+ * attributes for
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_get_reserves_attest (
+ struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub);
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.c b/src/exchange/taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.c
@@ -1,195 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.c
- * @brief Handle GET /reserves/$RESERVE_PUB/attest requests
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include "taler/taler_kyclogic_lib.h"
-#include "taler/taler_mhd_lib.h"
-#include "taler/taler_json_lib.h"
-#include "taler/taler_dbevents.h"
-#include "taler-exchange-httpd_get-keys.h"
-#include "taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.h"
-#include "taler-exchange-httpd_responses.h"
-
-
-/**
- * Closure for #reserve_attest_transaction.
- */
-struct ReserveAttestContext
-{
- /**
- * Public key of the reserve the inquiry is about.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
- /**
- * Hash of the payto URI of this reserve.
- */
- struct TALER_NormalizedPaytoHashP h_payto;
-
- /**
- * Available attributes.
- */
- json_t *attributes;
-
-};
-
-
-/**
- * Function called with information about all applicable
- * legitimization processes for the given user.
- *
- * @param cls our `struct ReserveAttestContext *`
- * @param h_payto account for which the attribute data is stored
- * @param provider_name provider that must be checked
- * @param collection_time when was the data collected
- * @param expiration_time when does the data expire
- * @param enc_attributes_size number of bytes in @a enc_attributes
- * @param enc_attributes encrypted attribute data
- */
-static void
-kyc_process_cb (void *cls,
- const struct TALER_NormalizedPaytoHashP *h_payto,
- const char *provider_name,
- struct GNUNET_TIME_Timestamp collection_time,
- struct GNUNET_TIME_Timestamp expiration_time,
- size_t enc_attributes_size,
- const void *enc_attributes)
-{
- struct ReserveAttestContext *rsc = cls;
- json_t *attrs;
- json_t *val;
- const char *name;
-
- if (GNUNET_TIME_absolute_is_past (
- expiration_time.abs_time))
- return;
- attrs = TALER_CRYPTO_kyc_attributes_decrypt (
- &TEH_attribute_key,
- enc_attributes,
- enc_attributes_size);
- json_object_foreach (attrs, name, val)
- {
- bool duplicate = false;
- size_t idx;
- json_t *str;
-
- json_array_foreach (rsc->attributes, idx, str)
- {
- if (0 == strcmp (json_string_value (str),
- name))
- {
- duplicate = true;
- break;
- }
- }
- if (duplicate)
- continue;
- GNUNET_assert (0 ==
- json_array_append_new (rsc->attributes,
- json_string (name)));
- }
- json_decref (attrs);
-}
-
-
-MHD_RESULT
-TEH_handler_reserves_get_attest (
- struct TEH_RequestContext *rc,
- const char *const args[1])
-{
- struct ReserveAttestContext rsc = {
- .attributes = NULL
- };
-
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (
- args[0],
- strlen (args[0]),
- &rsc.reserve_pub,
- sizeof (rsc.reserve_pub)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
- args[0]);
- }
- {
- struct TALER_NormalizedPayto payto_uri;
-
- payto_uri
- = TALER_reserve_make_payto (TEH_base_url,
- &rsc.reserve_pub);
- TALER_normalized_payto_hash (payto_uri,
- &rsc.h_payto);
- GNUNET_free (payto_uri.normalized_payto);
- }
- {
- enum GNUNET_DB_QueryStatus qs;
-
- rsc.attributes = json_array ();
- GNUNET_assert (NULL != rsc.attributes);
- qs = TEH_plugin->select_kyc_attributes (TEH_plugin->cls,
- &rsc.h_payto,
- &kyc_process_cb,
- &rsc);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- json_decref (rsc.attributes);
- rsc.attributes = NULL;
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "select_kyc_attributes");
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- json_decref (rsc.attributes);
- rsc.attributes = NULL;
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "select_kyc_attributes");
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- GNUNET_break_op (0);
- json_decref (rsc.attributes);
- rsc.attributes = NULL;
- return TALER_MHD_reply_with_error (
- rc->connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
- NULL);
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- break;
- }
- }
- return TALER_MHD_REPLY_JSON_PACK (
- rc->connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_array_steal ("details",
- rsc.attributes));
-}
-
-
-/* end of taler-exchange-httpd_reserves_get_attest.c */
diff --git a/src/exchange/taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.h b/src/exchange/taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.h
@@ -1,44 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2020 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_get-reserves-attest-RESERVE_PUB.h
- * @brief Handle /reserves/$RESERVE_PUB GET_ATTEST requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#ifndef TALER_EXCHANGE_HTTPD_GET_RESERVES_ATTEST_RESERVE_PUB_H
-#define TALER_EXCHANGE_HTTPD_GET_RESERVES_ATTEST_RESERVE_PUB_H
-
-#include <microhttpd.h>
-#include "taler-exchange-httpd.h"
-
-
-/**
- * Handle a GET "/reserves/$RID/attest" request. Parses the
- * given "reserve_pub" in @a args (which should contain the
- * EdDSA public key of a reserve) and then responds with the
- * available attestations for the reserve.
- *
- * @param rc request context
- * @param args array of additional options (length: 1, just the reserve_pub)
- * @return MHD result code
- */
-MHD_RESULT
-TEH_handler_reserves_get_attest (struct TEH_RequestContext *rc,
- const char *const args[1]);
-
-#endif
diff --git a/src/exchange/taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.c b/src/exchange/taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.c
@@ -0,0 +1,384 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2022, 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.c
+ * @brief Handle /reserves/$RESERVE_PUB/attest requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler/taler_dbevents.h"
+#include "taler/taler_kyclogic_lib.h"
+#include "taler/taler_json_lib.h"
+#include "taler/taler_mhd_lib.h"
+#include "taler-exchange-httpd_get-keys.h"
+#include "taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * How far do we allow a client's time to be off when
+ * checking the request timestamp?
+ */
+#define TIMESTAMP_TOLERANCE \
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
+
+
+/**
+ * Closure for #reserve_attest_transaction.
+ */
+struct ReserveAttestContext
+{
+ /**
+ * Public key of the reserve the inquiry is about.
+ */
+ const struct TALER_ReservePublicKeyP *reserve_pub;
+
+ /**
+ * Hash of the payto URI of this reserve.
+ */
+ struct TALER_NormalizedPaytoHashP h_payto;
+
+ /**
+ * Timestamp of the request.
+ */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /**
+ * Expiration time for the attestation.
+ */
+ struct GNUNET_TIME_Timestamp etime;
+
+ /**
+ * List of requested details.
+ */
+ const json_t *details;
+
+ /**
+ * Client signature approving the request.
+ */
+ struct TALER_ReserveSignatureP reserve_sig;
+
+ /**
+ * Attributes we are affirming. JSON object.
+ */
+ json_t *json_attest;
+
+ /**
+ * Database error codes encountered.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+
+ /**
+ * Set to true if we did not find the reserve.
+ */
+ bool not_found;
+
+};
+
+
+/**
+ * Send reserve attest to client.
+ *
+ * @param connection connection to the client
+ * @param rhc reserve attest to return
+ * @return MHD result code
+ */
+static MHD_RESULT
+reply_reserve_attest_success (struct MHD_Connection *connection,
+ const struct ReserveAttestContext *rhc)
+{
+ struct TALER_ExchangeSignatureP exchange_sig;
+ struct TALER_ExchangePublicKeyP exchange_pub;
+ enum TALER_ErrorCode ec;
+ struct GNUNET_TIME_Timestamp now;
+
+ if (NULL == rhc->json_attest)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
+ NULL);
+ }
+ now = GNUNET_TIME_timestamp_get ();
+ ec = TALER_exchange_online_reserve_attest_details_sign (
+ &TEH_keys_exchange_sign_,
+ now,
+ rhc->etime,
+ rhc->reserve_pub,
+ rhc->json_attest,
+ &exchange_pub,
+ &exchange_sig);
+ if (TALER_EC_NONE != ec)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_ec (connection,
+ ec,
+ NULL);
+ }
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_data_auto ("exchange_sig",
+ &exchange_sig),
+ GNUNET_JSON_pack_data_auto ("exchange_pub",
+ &exchange_pub),
+ GNUNET_JSON_pack_timestamp ("exchange_timestamp",
+ now),
+ GNUNET_JSON_pack_timestamp ("expiration_time",
+ rhc->etime),
+ GNUNET_JSON_pack_object_steal ("attributes",
+ rhc->json_attest));
+}
+
+
+/**
+ * Function called with information about all applicable
+ * legitimization processes for the given user. Finds the
+ * available attributes and merges them into our result
+ * set based on the details requested by the client.
+ *
+ * @param cls our `struct ReserveAttestContext *`
+ * @param h_payto account for which the attribute data is stored
+ * @param provider_name provider that must be checked
+ * @param collection_time when was the data collected
+ * @param expiration_time when does the data expire
+ * @param enc_attributes_size number of bytes in @a enc_attributes
+ * @param enc_attributes encrypted attribute data
+ */
+static void
+kyc_process_cb (void *cls,
+ const struct TALER_NormalizedPaytoHashP *h_payto,
+ const char *provider_name,
+ struct GNUNET_TIME_Timestamp collection_time,
+ struct GNUNET_TIME_Timestamp expiration_time,
+ size_t enc_attributes_size,
+ const void *enc_attributes)
+{
+ struct ReserveAttestContext *rsc = cls;
+ json_t *attrs;
+ json_t *val;
+ const char *name;
+ bool match = false;
+
+ if (GNUNET_TIME_absolute_is_past (expiration_time.abs_time))
+ return;
+ attrs = TALER_CRYPTO_kyc_attributes_decrypt (&TEH_attribute_key,
+ enc_attributes,
+ enc_attributes_size);
+ if (NULL == attrs)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ json_object_foreach (attrs, name, val)
+ {
+ bool requested = strcmp (name,
+ "FORM_ID"); /* we always return the FORM_ID */
+ size_t idx;
+ json_t *str;
+
+ if (NULL != json_object_get (rsc->json_attest,
+ name))
+ continue; /* duplicate */
+ json_array_foreach (rsc->details, idx, str)
+ {
+ if (0 == strcmp (json_string_value (str),
+ name))
+ {
+ requested = true;
+ break;
+ }
+ }
+ if (! requested)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Skipping attribute `%s': not requested\n",
+ name);
+ continue;
+ }
+ match = true;
+ GNUNET_assert (0 ==
+ json_object_set (rsc->json_attest, /* NOT set_new! */
+ name,
+ val));
+ }
+ json_decref (attrs);
+ if (! match)
+ return;
+ rsc->etime = GNUNET_TIME_timestamp_min (expiration_time,
+ rsc->etime);
+}
+
+
+/**
+ * Function implementing /reserves/$RID/attest transaction. Given the public
+ * key of a reserve, return the associated transaction attest. Runs the
+ * transaction logic; IF it returns a non-error code, the transaction logic
+ * MUST NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls a `struct ReserveAttestContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!); unused
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+reserve_attest_transaction (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct ReserveAttestContext *rsc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ rsc->json_attest = json_object ();
+ GNUNET_assert (NULL != rsc->json_attest);
+ qs = TEH_plugin->select_kyc_attributes (TEH_plugin->cls,
+ &rsc->h_payto,
+ &kyc_process_cb,
+ rsc);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "select_kyc_attributes");
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ rsc->not_found = true;
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ rsc->not_found = false;
+ break;
+ }
+ return qs;
+}
+
+
+MHD_RESULT
+TEH_handler_reserves_attest (
+ struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *root)
+{
+ struct ReserveAttestContext rsc = {
+ .etime = GNUNET_TIME_UNIT_FOREVER_TS,
+ .reserve_pub = reserve_pub
+ };
+ MHD_RESULT mhd_ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("request_timestamp",
+ &rsc.timestamp),
+ GNUNET_JSON_spec_array_const ("details",
+ &rsc.details),
+ GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+ &rsc.reserve_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ struct GNUNET_TIME_Timestamp now;
+
+ {
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (rc->connection,
+ root,
+ spec);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO; /* hard failure */
+ }
+ if (GNUNET_NO == res)
+ {
+ GNUNET_break_op (0);
+ return MHD_YES; /* failure */
+ }
+ }
+ now = GNUNET_TIME_timestamp_get ();
+ if (! GNUNET_TIME_absolute_approx_eq (now.abs_time,
+ rsc.timestamp.abs_time,
+ TIMESTAMP_TOLERANCE))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_CLOCK_SKEW,
+ NULL);
+ }
+
+ if (GNUNET_OK !=
+ TALER_wallet_reserve_attest_request_verify (rsc.timestamp,
+ rsc.details,
+ rsc.reserve_pub,
+ &rsc.reserve_sig))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_RESERVES_ATTEST_BAD_SIGNATURE,
+ NULL);
+ }
+
+ {
+ struct TALER_NormalizedPayto payto_uri;
+
+ payto_uri = TALER_reserve_make_payto (TEH_base_url,
+ rsc.reserve_pub);
+ TALER_normalized_payto_hash (payto_uri,
+ &rsc.h_payto);
+ GNUNET_free (payto_uri.normalized_payto);
+ }
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (rc->connection,
+ "post reserve attest",
+ TEH_MT_REQUEST_OTHER,
+ &mhd_ret,
+ &reserve_attest_transaction,
+ &rsc))
+ {
+ return mhd_ret;
+ }
+ if (rsc.not_found)
+ {
+ json_decref (rsc.json_attest);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
+ NULL);
+ }
+ return reply_reserve_attest_success (rc->connection,
+ &rsc);
+}
+
+
+/* end of taler-exchange-httpd_reserves-RESERVE_PUB-attest.c */
diff --git a/src/exchange/taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.h b/src/exchange/taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.h
@@ -0,0 +1,42 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.h
+ * @brief Handle /reserves/$RESERVE_PUB/attest requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_POST_RESERVES_RESERVE_PUB_ATTEST_H
+#define TALER_EXCHANGE_HTTPD_POST_RESERVES_RESERVE_PUB_ATTEST_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a POST "/reserves/$RID/attest" request.
+ *
+ * @param rc request context
+ * @param reserve_pub public key of the reserve
+ * @param root uploaded body from the client
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_reserves_attest (
+ struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *root);
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.c b/src/exchange/taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.c
@@ -1,391 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2022, 2024 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.c
- * @brief Handle /reserves/$RESERVE_PUB/attest requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include "taler/taler_dbevents.h"
-#include "taler/taler_kyclogic_lib.h"
-#include "taler/taler_json_lib.h"
-#include "taler/taler_mhd_lib.h"
-#include "taler-exchange-httpd_get-keys.h"
-#include "taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.h"
-#include "taler-exchange-httpd_responses.h"
-
-
-/**
- * How far do we allow a client's time to be off when
- * checking the request timestamp?
- */
-#define TIMESTAMP_TOLERANCE \
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
-
-
-/**
- * Closure for #reserve_attest_transaction.
- */
-struct ReserveAttestContext
-{
- /**
- * Public key of the reserve the inquiry is about.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
- /**
- * Hash of the payto URI of this reserve.
- */
- struct TALER_NormalizedPaytoHashP h_payto;
-
- /**
- * Timestamp of the request.
- */
- struct GNUNET_TIME_Timestamp timestamp;
-
- /**
- * Expiration time for the attestation.
- */
- struct GNUNET_TIME_Timestamp etime;
-
- /**
- * List of requested details.
- */
- const json_t *details;
-
- /**
- * Client signature approving the request.
- */
- struct TALER_ReserveSignatureP reserve_sig;
-
- /**
- * Attributes we are affirming. JSON object.
- */
- json_t *json_attest;
-
- /**
- * Database error codes encountered.
- */
- enum GNUNET_DB_QueryStatus qs;
-
- /**
- * Set to true if we did not find the reserve.
- */
- bool not_found;
-
-};
-
-
-/**
- * Send reserve attest to client.
- *
- * @param connection connection to the client
- * @param rhc reserve attest to return
- * @return MHD result code
- */
-static MHD_RESULT
-reply_reserve_attest_success (struct MHD_Connection *connection,
- const struct ReserveAttestContext *rhc)
-{
- struct TALER_ExchangeSignatureP exchange_sig;
- struct TALER_ExchangePublicKeyP exchange_pub;
- enum TALER_ErrorCode ec;
- struct GNUNET_TIME_Timestamp now;
-
- if (NULL == rhc->json_attest)
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
- NULL);
- }
- now = GNUNET_TIME_timestamp_get ();
- ec = TALER_exchange_online_reserve_attest_details_sign (
- &TEH_keys_exchange_sign_,
- now,
- rhc->etime,
- &rhc->reserve_pub,
- rhc->json_attest,
- &exchange_pub,
- &exchange_sig);
- if (TALER_EC_NONE != ec)
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_ec (connection,
- ec,
- NULL);
- }
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_data_auto ("exchange_sig",
- &exchange_sig),
- GNUNET_JSON_pack_data_auto ("exchange_pub",
- &exchange_pub),
- GNUNET_JSON_pack_timestamp ("exchange_timestamp",
- now),
- GNUNET_JSON_pack_timestamp ("expiration_time",
- rhc->etime),
- GNUNET_JSON_pack_object_steal ("attributes",
- rhc->json_attest));
-}
-
-
-/**
- * Function called with information about all applicable
- * legitimization processes for the given user. Finds the
- * available attributes and merges them into our result
- * set based on the details requested by the client.
- *
- * @param cls our `struct ReserveAttestContext *`
- * @param h_payto account for which the attribute data is stored
- * @param provider_name provider that must be checked
- * @param collection_time when was the data collected
- * @param expiration_time when does the data expire
- * @param enc_attributes_size number of bytes in @a enc_attributes
- * @param enc_attributes encrypted attribute data
- */
-static void
-kyc_process_cb (void *cls,
- const struct TALER_NormalizedPaytoHashP *h_payto,
- const char *provider_name,
- struct GNUNET_TIME_Timestamp collection_time,
- struct GNUNET_TIME_Timestamp expiration_time,
- size_t enc_attributes_size,
- const void *enc_attributes)
-{
- struct ReserveAttestContext *rsc = cls;
- json_t *attrs;
- json_t *val;
- const char *name;
- bool match = false;
-
- if (GNUNET_TIME_absolute_is_past (expiration_time.abs_time))
- return;
- attrs = TALER_CRYPTO_kyc_attributes_decrypt (&TEH_attribute_key,
- enc_attributes,
- enc_attributes_size);
- if (NULL == attrs)
- {
- GNUNET_break (0);
- return;
- }
- json_object_foreach (attrs, name, val)
- {
- bool requested = strcmp (name,
- "FORM_ID"); /* we always return the FORM_ID */
- size_t idx;
- json_t *str;
-
- if (NULL != json_object_get (rsc->json_attest,
- name))
- continue; /* duplicate */
- json_array_foreach (rsc->details, idx, str)
- {
- if (0 == strcmp (json_string_value (str),
- name))
- {
- requested = true;
- break;
- }
- }
- if (! requested)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Skipping attribute `%s': not requested\n",
- name);
- continue;
- }
- match = true;
- GNUNET_assert (0 ==
- json_object_set (rsc->json_attest, /* NOT set_new! */
- name,
- val));
- }
- json_decref (attrs);
- if (! match)
- return;
- rsc->etime = GNUNET_TIME_timestamp_min (expiration_time,
- rsc->etime);
-}
-
-
-/**
- * Function implementing /reserves/$RID/attest transaction. Given the public
- * key of a reserve, return the associated transaction attest. Runs the
- * transaction logic; IF it returns a non-error code, the transaction logic
- * MUST NOT queue a MHD response. IF it returns an hard error, the
- * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
- * returns the soft error code, the function MAY be called again to retry and
- * MUST not queue a MHD response.
- *
- * @param cls a `struct ReserveAttestContext *`
- * @param connection MHD request which triggered the transaction
- * @param[out] mhd_ret set to MHD response status for @a connection,
- * if transaction failed (!); unused
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-reserve_attest_transaction (void *cls,
- struct MHD_Connection *connection,
- MHD_RESULT *mhd_ret)
-{
- struct ReserveAttestContext *rsc = cls;
- enum GNUNET_DB_QueryStatus qs;
-
- rsc->json_attest = json_object ();
- GNUNET_assert (NULL != rsc->json_attest);
- qs = TEH_plugin->select_kyc_attributes (TEH_plugin->cls,
- &rsc->h_payto,
- &kyc_process_cb,
- rsc);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- *mhd_ret
- = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "select_kyc_attributes");
- return qs;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- return qs;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- rsc->not_found = true;
- return qs;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- rsc->not_found = false;
- break;
- }
- return qs;
-}
-
-
-MHD_RESULT
-TEH_handler_reserves_attest (struct TEH_RequestContext *rc,
- const json_t *root,
- const char *const args[1])
-{
- struct ReserveAttestContext rsc = {
- .etime = GNUNET_TIME_UNIT_FOREVER_TS
- };
- MHD_RESULT mhd_ret;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_timestamp ("request_timestamp",
- &rsc.timestamp),
- GNUNET_JSON_spec_array_const ("details",
- &rsc.details),
- GNUNET_JSON_spec_fixed_auto ("reserve_sig",
- &rsc.reserve_sig),
- GNUNET_JSON_spec_end ()
- };
- struct GNUNET_TIME_Timestamp now;
-
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (args[0],
- strlen (args[0]),
- &rsc.reserve_pub,
- sizeof (rsc.reserve_pub)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
- args[0]);
- }
- {
- enum GNUNET_GenericReturnValue res;
-
- res = TALER_MHD_parse_json_data (rc->connection,
- root,
- spec);
- if (GNUNET_SYSERR == res)
- {
- GNUNET_break (0);
- return MHD_NO; /* hard failure */
- }
- if (GNUNET_NO == res)
- {
- GNUNET_break_op (0);
- return MHD_YES; /* failure */
- }
- }
- now = GNUNET_TIME_timestamp_get ();
- if (! GNUNET_TIME_absolute_approx_eq (now.abs_time,
- rsc.timestamp.abs_time,
- TIMESTAMP_TOLERANCE))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_GENERIC_CLOCK_SKEW,
- NULL);
- }
-
- if (GNUNET_OK !=
- TALER_wallet_reserve_attest_request_verify (rsc.timestamp,
- rsc.details,
- &rsc.reserve_pub,
- &rsc.reserve_sig))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_RESERVES_ATTEST_BAD_SIGNATURE,
- NULL);
- }
-
- {
- struct TALER_NormalizedPayto payto_uri;
-
- payto_uri = TALER_reserve_make_payto (TEH_base_url,
- &rsc.reserve_pub);
- TALER_normalized_payto_hash (payto_uri,
- &rsc.h_payto);
- GNUNET_free (payto_uri.normalized_payto);
- }
-
- if (GNUNET_OK !=
- TEH_DB_run_transaction (rc->connection,
- "post reserve attest",
- TEH_MT_REQUEST_OTHER,
- &mhd_ret,
- &reserve_attest_transaction,
- &rsc))
- {
- return mhd_ret;
- }
- if (rsc.not_found)
- {
- json_decref (rsc.json_attest);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
- args[0]);
- }
- return reply_reserve_attest_success (rc->connection,
- &rsc);
-}
-
-
-/* end of taler-exchange-httpd_reserves_attest-RESERVE_PUB.c */
diff --git a/src/exchange/taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.h b/src/exchange/taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.h
@@ -1,41 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_post-reserves-attest-RESERVE_PUB.h
- * @brief Handle /reserves/$RESERVE_PUB/attest requests
- * @author Christian Grothoff
- */
-#ifndef TALER_EXCHANGE_HTTPD_POST_RESERVES_ATTEST_RESERVE_PUB_H
-#define TALER_EXCHANGE_HTTPD_POST_RESERVES_ATTEST_RESERVE_PUB_H
-
-#include <microhttpd.h>
-#include "taler-exchange-httpd.h"
-
-
-/**
- * Handle a POST "/reserves-attest/$RID" request.
- *
- * @param rc request context
- * @param root uploaded body from the client
- * @param args args[0] has public key of the reserve
- * @return MHD result code
- */
-MHD_RESULT
-TEH_handler_reserves_attest (struct TEH_RequestContext *rc,
- const json_t *root,
- const char *const args[1]);
-
-#endif
diff --git a/src/exchange/taler-exchange-httpd_post-reveal-melt.c b/src/exchange/taler-exchange-httpd_post-reveal-melt.c
@@ -123,30 +123,37 @@ find_original_refresh (
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
return GNUNET_OK; /* Only happy case */
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN,
+ NULL);
return GNUNET_SYSERR;
case GNUNET_DB_STATUS_HARD_ERROR:
- *result = TALER_MHD_reply_with_ec (connection,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get_refresh");
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get_refresh");
return GNUNET_SYSERR;
case GNUNET_DB_STATUS_SOFT_ERROR:
break; /* try again */
default:
GNUNET_break (0);
- *result = TALER_MHD_reply_with_ec (connection,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
return GNUNET_SYSERR;
}
}
/* after unsuccessful retries*/
- *result = TALER_MHD_reply_with_ec (connection,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get_refresh");
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get_refresh");
return GNUNET_SYSERR;
}
@@ -170,9 +177,11 @@ compare_age_commitment (
if (actx->no_age_commitment !=
actx->refresh.coin.no_age_commitment)
{
- *mhd_ret = TALER_MHD_reply_with_ec (connection,
- TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
- NULL);
+ *mhd_ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
+ NULL);
return GNUNET_SYSERR;
}
if (! actx->no_age_commitment)
@@ -188,9 +197,11 @@ compare_age_commitment (
&ach))
{
GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_ec (connection,
- TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
- NULL);
+ *mhd_ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
+ NULL);
return GNUNET_SYSERR;
}
}
@@ -285,11 +296,11 @@ calculate_blinded_detail (
if (GNUNET_OK != ret)
{
GNUNET_break (0);
- *result = TALER_MHD_REPLY_JSON_PACK (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- GNUNET_JSON_pack_string (
- "details",
- "failed to prepare planchet from base key"));
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "failed to prepare planchet from base key");
return ret;
}
}
@@ -362,10 +373,11 @@ verify_commitment (
{
if (NULL == signatures)
{
- *result = TALER_MHD_reply_with_error (con,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "signatures missing");
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "signatures missing");
return GNUNET_SYSERR;
}
}
@@ -373,10 +385,11 @@ verify_commitment (
{
if (NULL == rev_batch_seeds)
{
- *result = TALER_MHD_reply_with_error (con,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "batch_seeds missing");
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "batch_seeds missing");
return GNUNET_SYSERR;
}
}
@@ -386,9 +399,11 @@ verify_commitment (
keys = TEH_keys_get_state ();
if (NULL == keys)
{
- *result = TALER_MHD_reply_with_ec (con,
- TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ NULL);
return GNUNET_SYSERR;
}
@@ -405,9 +420,11 @@ verify_commitment (
if (NULL == denom_keys[i])
{
GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_ec (con,
- TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ NULL);
return GNUNET_SYSERR;
}
@@ -437,9 +454,11 @@ verify_commitment (
if (cs_count != rf->num_cs_r_values)
{
GNUNET_break (0);
- *result = TALER_MHD_reply_with_ec (con,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
return GNUNET_SYSERR;
}
/**
@@ -477,9 +496,11 @@ verify_commitment (
&(*signatures)[sig_idx++]))
{
GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_ec (con,
- TALER_EC_EXCHANGE_REFRESHES_REVEAL_LINK_SIGNATURE_INVALID,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_REFRESHES_REVEAL_LINK_SIGNATURE_INVALID,
+ NULL);
return GNUNET_SYSERR;
}
}
@@ -642,8 +663,8 @@ verify_commitment (
TALER_refresh_get_commitment (&rc,
&rf->refresh_seed,
rf->no_blinding_seed
- ? NULL
- : &rf->blinding_seed,
+ ? NULL
+ : &rf->blinding_seed,
&kappa_transfer_pubs,
&kappa_planchets_h,
&rf->coin.coin_pub,
@@ -655,9 +676,11 @@ verify_commitment (
&rc.session_hash))
{
GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_ec (con,
- TALER_EC_EXCHANGE_REFRESHES_REVEAL_INVALID_RCH,
- "rc");
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_REFRESHES_REVEAL_INVALID_RCH,
+ "rc");
return GNUNET_SYSERR;
}
}
@@ -692,30 +715,37 @@ commit_reveal (
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
return GNUNET_OK; /* Only happy case */
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- *result = TALER_MHD_reply_with_error (con,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN,
+ NULL);
return GNUNET_SYSERR;
case GNUNET_DB_STATUS_HARD_ERROR:
- *result = TALER_MHD_reply_with_ec (con,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "mark_refresh_reveal_success");
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "mark_refresh_reveal_success");
return GNUNET_SYSERR;
case GNUNET_DB_STATUS_SOFT_ERROR:
break; /* try again */
default:
GNUNET_break (0);
- *result = TALER_MHD_reply_with_ec (con,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
return GNUNET_SYSERR;
}
}
/* after unsuccessful retries*/
- *result = TALER_MHD_reply_with_ec (con,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "mark_refresh_reveal_success");
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "mark_refresh_reveal_success");
return GNUNET_SYSERR;
}
diff --git a/src/exchange/taler-exchange-httpd_post-reveal-withdraw.c b/src/exchange/taler-exchange-httpd_post-reveal-withdraw.c
@@ -119,10 +119,11 @@ parse_withdraw_reveal_json (
NULL)) )
{
GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- error);
+ *mhd_ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ error);
return GNUNET_SYSERR;
}
@@ -161,34 +162,38 @@ find_original_withdraw (
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
return GNUNET_OK; /* Only happy case */
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_WITHDRAW_COMMITMENT_UNKNOWN,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_WITHDRAW_COMMITMENT_UNKNOWN,
+ NULL);
return GNUNET_SYSERR;
case GNUNET_DB_STATUS_HARD_ERROR:
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get_withdraw");
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get_withdraw");
return GNUNET_SYSERR;
case GNUNET_DB_STATUS_SOFT_ERROR:
break; /* try again */
default:
GNUNET_break (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
return GNUNET_SYSERR;
}
}
/* after unsuccessful retries*/
GNUNET_break (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get_withdraw");
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get_withdraw");
return GNUNET_SYSERR;
}
@@ -270,11 +275,11 @@ calculate_blinded_hash (
if (GNUNET_OK != ret)
{
GNUNET_break (0);
- *result = TALER_MHD_REPLY_JSON_PACK (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- GNUNET_JSON_pack_string (
- "details",
- "failed to prepare planchet from base key"));
+ *result = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "failed to prepare planchet from base key");
return ret;
}
@@ -351,10 +356,11 @@ verify_commitment_and_max_age (
keys = TEH_keys_get_state ();
if (NULL == keys)
{
- *result = TALER_MHD_reply_with_error (con,
- MHD_HTTP_SERVICE_UNAVAILABLE,
- TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ NULL);
return GNUNET_SYSERR;
}
@@ -368,10 +374,11 @@ verify_commitment_and_max_age (
if (NULL == denom_keys[i])
{
GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_error (con,
- MHD_HTTP_SERVICE_UNAVAILABLE,
- TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
- NULL);
+ *result = TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ NULL);
return GNUNET_SYSERR;
}
diff --git a/src/exchange/taler-exchange-httpd_post-withdraw.c b/src/exchange/taler-exchange-httpd_post-withdraw.c
@@ -1337,8 +1337,9 @@ phase_generate_reply_error (
return;
case WITHDRAW_ERROR_RESERVE_UNKNOWN:
finish_loop (wc,
- TALER_MHD_reply_with_ec (
+ TALER_MHD_reply_with_error (
wc->rc->connection,
+ MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
NULL));
return;
@@ -1384,36 +1385,41 @@ phase_generate_reply_error (
case WITHDRAW_ERROR_DENOMINATION_REVOKED:
GNUNET_break_op (0);
finish_loop (wc,
- TALER_MHD_reply_with_ec (
+ TALER_MHD_reply_with_error (
wc->rc->connection,
+ MHD_HTTP_GONE,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
NULL));
return;
case WITHDRAW_ERROR_CIPHER_MISMATCH:
finish_loop (wc,
- TALER_MHD_reply_with_ec (
+ TALER_MHD_reply_with_error (
wc->rc->connection,
+ MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
NULL));
return;
case WITHDRAW_ERROR_BLINDING_SEED_REQUIRED:
finish_loop (wc,
- TALER_MHD_reply_with_ec (
+ TALER_MHD_reply_with_error (
wc->rc->connection,
+ MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MISSING,
"blinding_seed"));
return;
case WITHDRAW_ERROR_CRYPTO_HELPER:
finish_loop (wc,
- TALER_MHD_reply_with_ec (
+ TALER_MHD_reply_with_error (
wc->rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
NULL));
return;
case WITHDRAW_ERROR_RESERVE_CIPHER_UNKNOWN:
finish_loop (wc,
- TALER_MHD_reply_with_ec (
+ TALER_MHD_reply_with_error (
wc->rc->connection,
+ MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
"cipher"));
return;
@@ -1426,8 +1432,9 @@ phase_generate_reply_error (
"denomination %s does not support age restriction",
GNUNET_h2s (&wc->error.details.denom_h->hash));
finish_loop (wc,
- TALER_MHD_reply_with_ec (
+ TALER_MHD_reply_with_error (
wc->rc->connection,
+ MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
msg));
return;
@@ -1513,8 +1520,9 @@ phase_generate_reply_error (
return;
case WITHDRAW_ERROR_RESERVE_SIGNATURE_INVALID:
finish_loop (wc,
- TALER_MHD_reply_with_ec (
+ TALER_MHD_reply_with_error (
wc->rc->connection,
+ MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID,
NULL));
return;
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
@@ -36,8 +36,8 @@ libtalerexchange_la_SOURCES = \
exchange_api_get-keys.c \
exchange_api_get-management-keys.c \
exchange_api_get-purses-PURSE_PUB-merge.c \
- exchange_api_get-reserves-attest-RESERVE_PUB.c \
exchange_api_get-reserves-RESERVE_PUB.c \
+ exchange_api_get-reserves-RESERVE_PUB-attest.c \
exchange_api_get-reserves-RESERVE_PUB-history.c \
exchange_api_get-transfers-WTID.c \
exchange_api_common.c exchange_api_common.h \
@@ -69,7 +69,7 @@ libtalerexchange_la_SOURCES = \
exchange_api_post-purses-PURSE_PUB-merge.c \
exchange_api_post-recoup-refresh.c \
exchange_api_post-recoup-withdraw.c \
- exchange_api_post-reserves-attest-RESERVE_PUB.c \
+ exchange_api_post-reserves-RESERVE_PUB-attest.c \
exchange_api_post-reserves-RESERVE_PUB-close.c \
exchange_api_post-reserves-RESERVE_PUB-open.c \
exchange_api_post-reserves-RESERVE_PUB-purse.c \
diff --git a/src/lib/exchange_api_get-reserves-RESERVE_PUB-attest.c b/src/lib/exchange_api_get-reserves-RESERVE_PUB-attest.c
@@ -0,0 +1,296 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2026 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/exchange_api_get-reserves-RESERVE_PUB-attest.c
+ * @brief Implementation of the GET /reserves/$RESERVE_PUB/attest request
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler/taler_exchange_service.h"
+#include "taler/taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler/taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A GET /reserves/$RESERVE_PUB/attest Handle
+ */
+struct TALER_EXCHANGE_GetReservesAttestHandle
+{
+
+ /**
+ * Base URL of the exchange.
+ */
+ char *base_url;
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * CURL context to use.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_EXCHANGE_GetReservesAttestCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ TALER_EXCHANGE_GET_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Public key of the reserve we are querying.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+};
+
+
+/**
+ * We received an #MHD_HTTP_OK status code. Handle the JSON response.
+ *
+ * @param grah handle of the request
+ * @param j JSON response
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_reserves_get_attestable_ok (
+ struct TALER_EXCHANGE_GetReservesAttestHandle *grah,
+ const json_t *j)
+{
+ struct TALER_EXCHANGE_GetReservesAttestResponse rs = {
+ .hr.reply = j,
+ .hr.http_status = MHD_HTTP_OK
+ };
+ const json_t *details;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("details",
+ &details),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (j,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ size_t dlen = json_array_size (details);
+ const char *attributes[GNUNET_NZL (dlen)];
+
+ for (unsigned int i = 0; i < dlen; i++)
+ {
+ json_t *detail = json_array_get (details,
+ i);
+ attributes[i] = json_string_value (detail);
+ if (NULL == attributes[i])
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ rs.details.ok.attributes_length = dlen;
+ rs.details.ok.attributes = attributes;
+ grah->cb (grah->cb_cls,
+ &rs);
+ grah->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /reserves/$RESERVE_PUB/attest request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_GetReservesAttestHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserves_get_attestable_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_EXCHANGE_GetReservesAttestHandle *grah = cls;
+ const json_t *j = response;
+ struct TALER_EXCHANGE_GetReservesAttestResponse rs = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
+ };
+
+ grah->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ handle_reserves_get_attestable_ok (grah,
+ j))
+ {
+ rs.hr.http_status = 0;
+ rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* This should never happen, either us or the exchange is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ case MHD_HTTP_CONFLICT:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_break_op (0);
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for reserves get_attestable\n",
+ (unsigned int) response_code,
+ (int) rs.hr.ec);
+ break;
+ }
+ if (NULL != grah->cb)
+ {
+ grah->cb (grah->cb_cls,
+ &rs);
+ grah->cb = NULL;
+ }
+ TALER_EXCHANGE_get_reserves_attest_cancel (grah);
+}
+
+
+struct TALER_EXCHANGE_GetReservesAttestHandle *
+TALER_EXCHANGE_get_reserves_attest_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const struct TALER_ReservePublicKeyP *reserve_pub)
+{
+ struct TALER_EXCHANGE_GetReservesAttestHandle *grah;
+
+ grah = GNUNET_new (struct TALER_EXCHANGE_GetReservesAttestHandle);
+ grah->ctx = ctx;
+ grah->base_url = GNUNET_strdup (url);
+ grah->reserve_pub = *reserve_pub;
+ return grah;
+}
+
+
+enum TALER_ErrorCode
+TALER_EXCHANGE_get_reserves_attest_start (
+ struct TALER_EXCHANGE_GetReservesAttestHandle *grah,
+ TALER_EXCHANGE_GetReservesAttestCallback cb,
+ TALER_EXCHANGE_GET_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls)
+{
+ char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
+ CURL *eh;
+
+ if (NULL != grah->job)
+ {
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ grah->cb = cb;
+ grah->cb_cls = cb_cls;
+ {
+ char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (
+ &grah->reserve_pub,
+ sizeof (grah->reserve_pub),
+ pub_str,
+ sizeof (pub_str));
+ *end = '\0';
+ GNUNET_snprintf (arg_str,
+ sizeof (arg_str),
+ "reserves/%s/attest",
+ pub_str);
+ }
+ grah->url = TALER_url_join (grah->base_url,
+ arg_str,
+ NULL);
+ if (NULL == grah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_EXCHANGE_curl_easy_get_ (grah->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ grah->job = GNUNET_CURL_job_add (grah->ctx,
+ eh,
+ &handle_reserves_get_attestable_finished,
+ grah);
+ if (NULL == grah->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_EXCHANGE_get_reserves_attest_cancel (
+ struct TALER_EXCHANGE_GetReservesAttestHandle *grah)
+{
+ if (NULL != grah->job)
+ {
+ GNUNET_CURL_job_cancel (grah->job);
+ grah->job = NULL;
+ }
+ GNUNET_free (grah->url);
+ GNUNET_free (grah->base_url);
+ GNUNET_free (grah);
+}
+
+
+/* end of exchange_api_get-reserves-RESERVE_PUB-attest.c */
diff --git a/src/lib/exchange_api_get-reserves-attest-RESERVE_PUB.c b/src/lib/exchange_api_get-reserves-attest-RESERVE_PUB.c
@@ -1,296 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2026 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, see
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/exchange_api_get-reserves-attest-RESERVE_PUB.c
- * @brief Implementation of the GET /reserves/$RESERVE_PUB/attest request
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler/taler_exchange_service.h"
-#include "taler/taler_json_lib.h"
-#include "exchange_api_handle.h"
-#include "taler/taler_signatures.h"
-#include "exchange_api_curl_defaults.h"
-
-
-/**
- * @brief A GET /reserves/$RESERVE_PUB/attest Handle
- */
-struct TALER_EXCHANGE_GetReservesAttestHandle
-{
-
- /**
- * Base URL of the exchange.
- */
- char *base_url;
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * CURL context to use.
- */
- struct GNUNET_CURL_Context *ctx;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_EXCHANGE_GetReservesAttestCallback cb;
-
- /**
- * Closure for @e cb.
- */
- TALER_EXCHANGE_GET_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls;
-
- /**
- * Public key of the reserve we are querying.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
-};
-
-
-/**
- * We received an #MHD_HTTP_OK status code. Handle the JSON response.
- *
- * @param grah handle of the request
- * @param j JSON response
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-handle_reserves_get_attestable_ok (
- struct TALER_EXCHANGE_GetReservesAttestHandle *grah,
- const json_t *j)
-{
- struct TALER_EXCHANGE_GetReservesAttestResponse rs = {
- .hr.reply = j,
- .hr.http_status = MHD_HTTP_OK
- };
- const json_t *details;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("details",
- &details),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (j,
- spec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- {
- size_t dlen = json_array_size (details);
- const char *attributes[GNUNET_NZL (dlen)];
-
- for (unsigned int i = 0; i < dlen; i++)
- {
- json_t *detail = json_array_get (details,
- i);
- attributes[i] = json_string_value (detail);
- if (NULL == attributes[i])
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- rs.details.ok.attributes_length = dlen;
- rs.details.ok.attributes = attributes;
- grah->cb (grah->cb_cls,
- &rs);
- grah->cb = NULL;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP GET /reserves/$RESERVE_PUB/attest request.
- *
- * @param cls the `struct TALER_EXCHANGE_GetReservesAttestHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_reserves_get_attestable_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_EXCHANGE_GetReservesAttestHandle *grah = cls;
- const json_t *j = response;
- struct TALER_EXCHANGE_GetReservesAttestResponse rs = {
- .hr.reply = j,
- .hr.http_status = (unsigned int) response_code
- };
-
- grah->job = NULL;
- switch (response_code)
- {
- case 0:
- rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- if (GNUNET_OK !=
- handle_reserves_get_attestable_ok (grah,
- j))
- {
- rs.hr.http_status = 0;
- rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- }
- break;
- case MHD_HTTP_BAD_REQUEST:
- /* This should never happen, either us or the exchange is buggy
- (or API version conflict); just pass JSON reply to the application */
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- case MHD_HTTP_NOT_FOUND:
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the application */
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- case MHD_HTTP_CONFLICT:
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the application */
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- /* Server had an internal issue; we should retry, but this API
- leaves this to the application */
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- default:
- /* unexpected response code */
- GNUNET_break_op (0);
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d for reserves get_attestable\n",
- (unsigned int) response_code,
- (int) rs.hr.ec);
- break;
- }
- if (NULL != grah->cb)
- {
- grah->cb (grah->cb_cls,
- &rs);
- grah->cb = NULL;
- }
- TALER_EXCHANGE_get_reserves_attest_cancel (grah);
-}
-
-
-struct TALER_EXCHANGE_GetReservesAttestHandle *
-TALER_EXCHANGE_get_reserves_attest_create (
- struct GNUNET_CURL_Context *ctx,
- const char *url,
- const struct TALER_ReservePublicKeyP *reserve_pub)
-{
- struct TALER_EXCHANGE_GetReservesAttestHandle *grah;
-
- grah = GNUNET_new (struct TALER_EXCHANGE_GetReservesAttestHandle);
- grah->ctx = ctx;
- grah->base_url = GNUNET_strdup (url);
- grah->reserve_pub = *reserve_pub;
- return grah;
-}
-
-
-enum TALER_ErrorCode
-TALER_EXCHANGE_get_reserves_attest_start (
- struct TALER_EXCHANGE_GetReservesAttestHandle *grah,
- TALER_EXCHANGE_GetReservesAttestCallback cb,
- TALER_EXCHANGE_GET_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls)
-{
- char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
- CURL *eh;
-
- if (NULL != grah->job)
- {
- GNUNET_break (0);
- return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
- }
- grah->cb = cb;
- grah->cb_cls = cb_cls;
- {
- char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
- char *end;
-
- end = GNUNET_STRINGS_data_to_string (
- &grah->reserve_pub,
- sizeof (grah->reserve_pub),
- pub_str,
- sizeof (pub_str));
- *end = '\0';
- GNUNET_snprintf (arg_str,
- sizeof (arg_str),
- "reserves-attest/%s",
- pub_str);
- }
- grah->url = TALER_url_join (grah->base_url,
- arg_str,
- NULL);
- if (NULL == grah->url)
- return TALER_EC_GENERIC_CONFIGURATION_INVALID;
- eh = TALER_EXCHANGE_curl_easy_get_ (grah->url);
- if (NULL == eh)
- return TALER_EC_GENERIC_CONFIGURATION_INVALID;
- grah->job = GNUNET_CURL_job_add (grah->ctx,
- eh,
- &handle_reserves_get_attestable_finished,
- grah);
- if (NULL == grah->job)
- return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
- return TALER_EC_NONE;
-}
-
-
-void
-TALER_EXCHANGE_get_reserves_attest_cancel (
- struct TALER_EXCHANGE_GetReservesAttestHandle *grah)
-{
- if (NULL != grah->job)
- {
- GNUNET_CURL_job_cancel (grah->job);
- grah->job = NULL;
- }
- GNUNET_free (grah->url);
- GNUNET_free (grah->base_url);
- GNUNET_free (grah);
-}
-
-
-/* end of exchange_api_get-reserves-attest-RESERVE_PUB.c */
diff --git a/src/lib/exchange_api_post-reserves-RESERVE_PUB-attest.c b/src/lib/exchange_api_post-reserves-RESERVE_PUB-attest.c
@@ -0,0 +1,393 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2026 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/exchange_api_post-reserves-RESERVE_PUB-attest.c
+ * @brief Implementation of the POST /reserves/$RESERVE_PUB/attest requests
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP attest codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler/taler_exchange_service.h"
+#include "taler/taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler/taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A POST /reserves/$RID/attest Handle
+ */
+struct TALER_EXCHANGE_PostReservesAttestHandle
+{
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Base URL of the exchange.
+ */
+ char *base_url;
+
+ /**
+ * The url for this request, set during _start.
+ */
+ char *url;
+
+ /**
+ * Context for #TEH_curl_easy_post(). Keeps the data that must
+ * persist for Curl to make the upload.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_EXCHANGE_PostReservesAttestCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_EXCHANGE_POST_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * The keys of the exchange this request handle will use.
+ */
+ struct TALER_EXCHANGE_Keys *keys;
+
+ /**
+ * Public key of the reserve we are querying.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Pre-built JSON body for the request.
+ */
+ json_t *body;
+
+};
+
+
+/**
+ * We received an #MHD_HTTP_OK attest code. Handle the JSON
+ * response.
+ *
+ * @param prah handle of the request
+ * @param j JSON response
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_reserves_attest_ok (struct TALER_EXCHANGE_PostReservesAttestHandle *prah,
+ const json_t *j)
+{
+ struct TALER_EXCHANGE_PostReservesAttestResponse rs = {
+ .hr.reply = j,
+ .hr.http_status = MHD_HTTP_OK
+ };
+ const json_t *attributes;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("exchange_timestamp",
+ &rs.details.ok.exchange_time),
+ GNUNET_JSON_spec_timestamp ("expiration_time",
+ &rs.details.ok.expiration_time),
+ GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+ &rs.details.ok.exchange_sig),
+ GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+ &rs.details.ok.exchange_pub),
+ GNUNET_JSON_spec_object_const ("attributes",
+ &attributes),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (j,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_EXCHANGE_test_signing_key (prah->keys,
+ &rs.details.ok.exchange_pub))
+ {
+ GNUNET_break_op (0);
+ rs.hr.http_status = 0;
+ rs.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
+ prah->cb (prah->cb_cls,
+ &rs);
+ prah->cb = NULL;
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ rs.details.ok.attributes = attributes;
+ if (GNUNET_OK !=
+ TALER_exchange_online_reserve_attest_details_verify (
+ rs.details.ok.exchange_time,
+ rs.details.ok.expiration_time,
+ &prah->reserve_pub,
+ attributes,
+ &rs.details.ok.exchange_pub,
+ &rs.details.ok.exchange_sig))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ prah->cb (prah->cb_cls,
+ &rs);
+ prah->cb = NULL;
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/$RID/attest request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_PostReservesAttestHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserves_attest_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_EXCHANGE_PostReservesAttestHandle *prah = cls;
+ const json_t *j = response;
+ struct TALER_EXCHANGE_PostReservesAttestResponse rs = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
+ };
+
+ prah->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ handle_reserves_attest_ok (prah,
+ j))
+ {
+ rs.hr.http_status = 0;
+ rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* This should never happen, either us or the exchange is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ GNUNET_break (0);
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ /* This should never happen, either us or the exchange is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ GNUNET_break (0);
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ case MHD_HTTP_CONFLICT:
+ /* Server doesn't have the requested attributes */
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_break_op (0);
+ rs.hr.ec = TALER_JSON_get_error_code (j);
+ rs.hr.hint = TALER_JSON_get_error_hint (j);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for reserves attest\n",
+ (unsigned int) response_code,
+ (int) rs.hr.ec);
+ break;
+ }
+ if (NULL != prah->cb)
+ {
+ prah->cb (prah->cb_cls,
+ &rs);
+ prah->cb = NULL;
+ }
+ TALER_EXCHANGE_post_reserves_attest_cancel (prah);
+}
+
+
+struct TALER_EXCHANGE_PostReservesAttestHandle *
+TALER_EXCHANGE_post_reserves_attest_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_ReservePrivateKeyP *reserve_priv,
+ unsigned int attributes_length,
+ const char *attributes[const static attributes_length])
+{
+ struct TALER_EXCHANGE_PostReservesAttestHandle *prah;
+ struct TALER_ReserveSignatureP reserve_sig;
+ json_t *details;
+ struct GNUNET_TIME_Timestamp ts;
+
+ if (0 == attributes_length)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ details = json_array ();
+ GNUNET_assert (NULL != details);
+ for (unsigned int i = 0; i < attributes_length; i++)
+ {
+ GNUNET_assert (0 ==
+ json_array_append_new (details,
+ json_string (attributes[i])));
+ }
+ prah = GNUNET_new (struct TALER_EXCHANGE_PostReservesAttestHandle);
+ prah->ctx = ctx;
+ prah->base_url = GNUNET_strdup (url);
+ prah->keys = TALER_EXCHANGE_keys_incref (keys);
+ GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+ &prah->reserve_pub.eddsa_pub);
+ ts = GNUNET_TIME_timestamp_get ();
+ TALER_wallet_reserve_attest_request_sign (ts,
+ details,
+ reserve_priv,
+ &reserve_sig);
+ prah->body = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("reserve_sig",
+ &reserve_sig),
+ GNUNET_JSON_pack_timestamp ("request_timestamp",
+ ts),
+ GNUNET_JSON_pack_array_steal ("details",
+ details));
+ if (NULL == prah->body)
+ {
+ GNUNET_break (0);
+ GNUNET_free (prah->base_url);
+ TALER_EXCHANGE_keys_decref (prah->keys);
+ GNUNET_free (prah);
+ return NULL;
+ }
+ return prah;
+}
+
+
+enum TALER_ErrorCode
+TALER_EXCHANGE_post_reserves_attest_start (
+ struct TALER_EXCHANGE_PostReservesAttestHandle *prah,
+ TALER_EXCHANGE_PostReservesAttestCallback cb,
+ TALER_EXCHANGE_POST_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+ char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
+
+ prah->cb = cb;
+ prah->cb_cls = cb_cls;
+ {
+ char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (
+ &prah->reserve_pub,
+ sizeof (prah->reserve_pub),
+ pub_str,
+ sizeof (pub_str));
+ *end = '\0';
+ GNUNET_snprintf (arg_str,
+ sizeof (arg_str),
+ "reserves/%s/attest",
+ pub_str);
+ }
+ prah->url = TALER_url_join (prah->base_url,
+ arg_str,
+ NULL);
+ if (NULL == prah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_EXCHANGE_curl_easy_get_ (prah->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&prah->post_ctx,
+ eh,
+ prah->body)) )
+ {
+ GNUNET_break (0);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ GNUNET_free (prah->url);
+ prah->url = NULL;
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ prah->job = GNUNET_CURL_job_add2 (prah->ctx,
+ eh,
+ prah->post_ctx.headers,
+ &handle_reserves_attest_finished,
+ prah);
+ if (NULL == prah->job)
+ {
+ TALER_curl_easy_post_finished (&prah->post_ctx);
+ GNUNET_free (prah->url);
+ prah->url = NULL;
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_EXCHANGE_post_reserves_attest_cancel (
+ struct TALER_EXCHANGE_PostReservesAttestHandle *prah)
+{
+ if (NULL != prah->job)
+ {
+ GNUNET_CURL_job_cancel (prah->job);
+ prah->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&prah->post_ctx);
+ json_decref (prah->body);
+ GNUNET_free (prah->url);
+ GNUNET_free (prah->base_url);
+ TALER_EXCHANGE_keys_decref (prah->keys);
+ GNUNET_free (prah);
+}
+
+
+/* end of exchange_api_post-reserves-RESERVE_PUB-attest.c */
diff --git a/src/lib/exchange_api_post-reserves-attest-RESERVE_PUB.c b/src/lib/exchange_api_post-reserves-attest-RESERVE_PUB.c
@@ -1,393 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2026 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, see
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/exchange_api_post-reserves-attest-RESERVE_PUB.c
- * @brief Implementation of the POST /reserves-attest/$RESERVE_PUB requests
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP attest codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler/taler_exchange_service.h"
-#include "taler/taler_json_lib.h"
-#include "exchange_api_handle.h"
-#include "taler/taler_signatures.h"
-#include "exchange_api_curl_defaults.h"
-
-
-/**
- * @brief A POST /reserves-attest/$RID Handle
- */
-struct TALER_EXCHANGE_PostReservesAttestHandle
-{
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
- /**
- * Base URL of the exchange.
- */
- char *base_url;
-
- /**
- * The url for this request, set during _start.
- */
- char *url;
-
- /**
- * Context for #TEH_curl_easy_post(). Keeps the data that must
- * persist for Curl to make the upload.
- */
- struct TALER_CURL_PostContext post_ctx;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_EXCHANGE_PostReservesAttestCallback cb;
-
- /**
- * Closure for @a cb.
- */
- TALER_EXCHANGE_POST_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls;
-
- /**
- * The keys of the exchange this request handle will use.
- */
- struct TALER_EXCHANGE_Keys *keys;
-
- /**
- * Public key of the reserve we are querying.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
- /**
- * Pre-built JSON body for the request.
- */
- json_t *body;
-
-};
-
-
-/**
- * We received an #MHD_HTTP_OK attest code. Handle the JSON
- * response.
- *
- * @param prah handle of the request
- * @param j JSON response
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-handle_reserves_attest_ok (struct TALER_EXCHANGE_PostReservesAttestHandle *prah,
- const json_t *j)
-{
- struct TALER_EXCHANGE_PostReservesAttestResponse rs = {
- .hr.reply = j,
- .hr.http_status = MHD_HTTP_OK
- };
- const json_t *attributes;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_timestamp ("exchange_timestamp",
- &rs.details.ok.exchange_time),
- GNUNET_JSON_spec_timestamp ("expiration_time",
- &rs.details.ok.expiration_time),
- GNUNET_JSON_spec_fixed_auto ("exchange_sig",
- &rs.details.ok.exchange_sig),
- GNUNET_JSON_spec_fixed_auto ("exchange_pub",
- &rs.details.ok.exchange_pub),
- GNUNET_JSON_spec_object_const ("attributes",
- &attributes),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (j,
- spec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_EXCHANGE_test_signing_key (prah->keys,
- &rs.details.ok.exchange_pub))
- {
- GNUNET_break_op (0);
- rs.hr.http_status = 0;
- rs.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
- prah->cb (prah->cb_cls,
- &rs);
- prah->cb = NULL;
- GNUNET_JSON_parse_free (spec);
- return GNUNET_SYSERR;
- }
- rs.details.ok.attributes = attributes;
- if (GNUNET_OK !=
- TALER_exchange_online_reserve_attest_details_verify (
- rs.details.ok.exchange_time,
- rs.details.ok.expiration_time,
- &prah->reserve_pub,
- attributes,
- &rs.details.ok.exchange_pub,
- &rs.details.ok.exchange_sig))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return GNUNET_SYSERR;
- }
- prah->cb (prah->cb_cls,
- &rs);
- prah->cb = NULL;
- GNUNET_JSON_parse_free (spec);
- return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /reserves-attest/$RID request.
- *
- * @param cls the `struct TALER_EXCHANGE_PostReservesAttestHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_reserves_attest_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_EXCHANGE_PostReservesAttestHandle *prah = cls;
- const json_t *j = response;
- struct TALER_EXCHANGE_PostReservesAttestResponse rs = {
- .hr.reply = j,
- .hr.http_status = (unsigned int) response_code
- };
-
- prah->job = NULL;
- switch (response_code)
- {
- case 0:
- rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- if (GNUNET_OK !=
- handle_reserves_attest_ok (prah,
- j))
- {
- rs.hr.http_status = 0;
- rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- }
- break;
- case MHD_HTTP_BAD_REQUEST:
- /* This should never happen, either us or the exchange is buggy
- (or API version conflict); just pass JSON reply to the application */
- GNUNET_break (0);
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- case MHD_HTTP_FORBIDDEN:
- /* This should never happen, either us or the exchange is buggy
- (or API version conflict); just pass JSON reply to the application */
- GNUNET_break (0);
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- case MHD_HTTP_NOT_FOUND:
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the application */
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- case MHD_HTTP_CONFLICT:
- /* Server doesn't have the requested attributes */
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- /* Server had an internal issue; we should retry, but this API
- leaves this to the application */
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- break;
- default:
- /* unexpected response code */
- GNUNET_break_op (0);
- rs.hr.ec = TALER_JSON_get_error_code (j);
- rs.hr.hint = TALER_JSON_get_error_hint (j);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d for reserves attest\n",
- (unsigned int) response_code,
- (int) rs.hr.ec);
- break;
- }
- if (NULL != prah->cb)
- {
- prah->cb (prah->cb_cls,
- &rs);
- prah->cb = NULL;
- }
- TALER_EXCHANGE_post_reserves_attest_cancel (prah);
-}
-
-
-struct TALER_EXCHANGE_PostReservesAttestHandle *
-TALER_EXCHANGE_post_reserves_attest_create (
- struct GNUNET_CURL_Context *ctx,
- const char *url,
- struct TALER_EXCHANGE_Keys *keys,
- const struct TALER_ReservePrivateKeyP *reserve_priv,
- unsigned int attributes_length,
- const char *attributes[const static attributes_length])
-{
- struct TALER_EXCHANGE_PostReservesAttestHandle *prah;
- struct TALER_ReserveSignatureP reserve_sig;
- json_t *details;
- struct GNUNET_TIME_Timestamp ts;
-
- if (0 == attributes_length)
- {
- GNUNET_break (0);
- return NULL;
- }
- details = json_array ();
- GNUNET_assert (NULL != details);
- for (unsigned int i = 0; i < attributes_length; i++)
- {
- GNUNET_assert (0 ==
- json_array_append_new (details,
- json_string (attributes[i])));
- }
- prah = GNUNET_new (struct TALER_EXCHANGE_PostReservesAttestHandle);
- prah->ctx = ctx;
- prah->base_url = GNUNET_strdup (url);
- prah->keys = TALER_EXCHANGE_keys_incref (keys);
- GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
- &prah->reserve_pub.eddsa_pub);
- ts = GNUNET_TIME_timestamp_get ();
- TALER_wallet_reserve_attest_request_sign (ts,
- details,
- reserve_priv,
- &reserve_sig);
- prah->body = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("reserve_sig",
- &reserve_sig),
- GNUNET_JSON_pack_timestamp ("request_timestamp",
- ts),
- GNUNET_JSON_pack_array_steal ("details",
- details));
- if (NULL == prah->body)
- {
- GNUNET_break (0);
- GNUNET_free (prah->base_url);
- TALER_EXCHANGE_keys_decref (prah->keys);
- GNUNET_free (prah);
- return NULL;
- }
- return prah;
-}
-
-
-enum TALER_ErrorCode
-TALER_EXCHANGE_post_reserves_attest_start (
- struct TALER_EXCHANGE_PostReservesAttestHandle *prah,
- TALER_EXCHANGE_PostReservesAttestCallback cb,
- TALER_EXCHANGE_POST_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls)
-{
- CURL *eh;
- char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
-
- prah->cb = cb;
- prah->cb_cls = cb_cls;
- {
- char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
- char *end;
-
- end = GNUNET_STRINGS_data_to_string (
- &prah->reserve_pub,
- sizeof (prah->reserve_pub),
- pub_str,
- sizeof (pub_str));
- *end = '\0';
- GNUNET_snprintf (arg_str,
- sizeof (arg_str),
- "reserves-attest/%s",
- pub_str);
- }
- prah->url = TALER_url_join (prah->base_url,
- arg_str,
- NULL);
- if (NULL == prah->url)
- return TALER_EC_GENERIC_CONFIGURATION_INVALID;
- eh = TALER_EXCHANGE_curl_easy_get_ (prah->url);
- if ( (NULL == eh) ||
- (GNUNET_OK !=
- TALER_curl_easy_post (&prah->post_ctx,
- eh,
- prah->body)) )
- {
- GNUNET_break (0);
- if (NULL != eh)
- curl_easy_cleanup (eh);
- GNUNET_free (prah->url);
- prah->url = NULL;
- return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
- }
- prah->job = GNUNET_CURL_job_add2 (prah->ctx,
- eh,
- prah->post_ctx.headers,
- &handle_reserves_attest_finished,
- prah);
- if (NULL == prah->job)
- {
- TALER_curl_easy_post_finished (&prah->post_ctx);
- GNUNET_free (prah->url);
- prah->url = NULL;
- return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
- }
- return TALER_EC_NONE;
-}
-
-
-void
-TALER_EXCHANGE_post_reserves_attest_cancel (
- struct TALER_EXCHANGE_PostReservesAttestHandle *prah)
-{
- if (NULL != prah->job)
- {
- GNUNET_CURL_job_cancel (prah->job);
- prah->job = NULL;
- }
- TALER_curl_easy_post_finished (&prah->post_ctx);
- json_decref (prah->body);
- GNUNET_free (prah->url);
- GNUNET_free (prah->base_url);
- TALER_EXCHANGE_keys_decref (prah->keys);
- GNUNET_free (prah);
-}
-
-
-/* end of exchange_api_post-reserves-attest-RESERVE_PUB.c */
diff --git a/src/lib/exchange_api_post-withdraw.c b/src/lib/exchange_api_post-withdraw.c
@@ -387,7 +387,6 @@ copy_results (
case MHD_HTTP_CREATED:
resp.details.created = wbr->details.created;
break;
-
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
resp.details.unavailable_for_legal_reasons =
wbr->details.unavailable_for_legal_reasons;
@@ -435,12 +434,6 @@ copy_results_with_age_proof (
/* in the age-restricted case, this should not happen */
GNUNET_break_op (0);
break;
-
- case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
- resp.details.unavailable_for_legal_reasons =
- wbr->details.unavailable_for_legal_reasons;
- break;
-
case MHD_HTTP_CREATED:
{
GNUNET_assert (wh->num_coins == wbr->details.created.num_coins);
@@ -457,7 +450,10 @@ copy_results_with_age_proof (
}
break;
}
-
+ case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
+ resp.details.unavailable_for_legal_reasons =
+ wbr->details.unavailable_for_legal_reasons;
+ break;
default:
break;
}
diff --git a/src/lib/exchange_api_post-withdraw_blinded.c b/src/lib/exchange_api_post-withdraw_blinded.c
@@ -432,6 +432,14 @@ handle_withdraw_blinded_finished (
wbr.hr.ec = TALER_JSON_get_error_code (j_response);
wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
break;
+ case MHD_HTTP_PRECONDITION_FAILED:
+ /* could happen if we were too early and the denomination
+ is not yet available */
+ /* Note: one might want to check the "Date" header to
+ see if our clock is very far off */
+ wbr.hr.ec = TALER_JSON_get_error_code (j_response);
+ wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
/* only validate reply is well-formed */
{
@@ -456,6 +464,7 @@ handle_withdraw_blinded_finished (
};
wbr.hr.ec = TALER_JSON_get_error_code (j_response);
+ wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
if (GNUNET_OK !=
GNUNET_JSON_parse (j_response,
spec,
@@ -474,6 +483,23 @@ handle_withdraw_blinded_finished (
wbr.hr.ec = TALER_JSON_get_error_code (j_response);
wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
break;
+ case MHD_HTTP_NOT_IMPLEMENTED:
+ /* Server does not implement a feature (usually the cipher) */
+ wbr.hr.ec = TALER_JSON_get_error_code (j_response);
+ wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
+ case MHD_HTTP_BAD_GATEWAY:
+ /* Server could not talk to another component, usually this
+ indicates a problem with the secmod helper */
+ wbr.hr.ec = TALER_JSON_get_error_code (j_response);
+ wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
+ case MHD_HTTP_SERVICE_UNAVAILABLE:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ wbr.hr.ec = TALER_JSON_get_error_code (j_response);
+ wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
default:
/* unexpected response code */
GNUNET_break_op (0);