challenger

OAuth 2.0-based authentication service that validates user can receive messages at a certain address
Log | Files | Refs | Submodules | README | LICENSE

commit a0dfdffe3b513b0d7791a456c5db528abd66abca
parent 05bbe5de3bb40ab82745f0b75d41af9aa43cae7d
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 15 Apr 2024 11:10:34 +0200

implement protocol v1

Diffstat:
Mconfigure.ac | 27+++++++++++++++++++++++++--
Msrc/challenger/challenger-httpd_authorize.c | 155++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/challenger/challenger-httpd_challenge.c | 150+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/challenger/challenger-httpd_common.c | 19+++++++++++++++++++
Msrc/challenger/challenger-httpd_common.h | 10++++++++++
Msrc/challenger/challenger-httpd_config.c | 5+++--
Msrc/challenger/challenger-httpd_solve.c | 131++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
7 files changed, 347 insertions(+), 150 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -4,7 +4,7 @@ # This configure file is in the public domain AC_PREREQ([2.69]) -AC_INIT([challenger],[0.10.1],[taler-bug@gnu.org]) +AC_INIT([challenger],[0.11.0],[taler-bug@gnu.org]) AC_CONFIG_SRCDIR([src/challenger/challenger-httpd.c]) AC_CONFIG_HEADERS([challenger_config.h]) # support for non-recursive builds @@ -76,7 +76,7 @@ AS_IF([test $libgnunetutil != 1], # test for postgres -AX_LIB_POSTGRESQL([13]) +AX_LIB_POSTGRESQL([14]) AS_IF([test "x$found_postgresql" = "xyes"],[postgres=true]) AM_CONDITIONAL(HAVE_POSTGRESQL, test x$postgres = xtrue) @@ -108,6 +108,29 @@ AC_CHECK_HEADERS([gnunet/gnunet_pq_lib.h], AM_CONDITIONAL(HAVE_GNUNETPQ, test x$libgnunetpq = x1) +libtalermhd=0 +AC_MSG_CHECKING([for libtalermhd]) +AC_ARG_WITH(exchange, + [AS_HELP_STRING([--with-exchange=PFX], [base of Taler EXCHANGE installation])], + [AC_MSG_RESULT([given as $with_exchange])], + [AC_MSG_RESULT(not given) + with_exchange=yes]) +AS_CASE([$with_exchange], + [yes], [], + [no], [AC_MSG_ERROR([--with-exchange is required])], + [LDFLAGS="-L$with_exchange/lib $LDFLAGS" + CPPFLAGS="-I$with_exchange/include $CPPFLAGS $POSTGRESQL_CPPFLAGS"]) + +AC_CHECK_HEADERS([taler/taler_mhd_lib.h], + [AC_CHECK_LIB([talermhd], [TALER_MHD_check_accept], libtalermhd=1)]) +AM_CONDITIONAL(HAVE_TALERMHD, test x$libtalermhd = x1) +AS_IF([test $libtalermhd != 1], + [AC_MSG_ERROR([[ +*** +*** You need libtalermhd >= 0.10.3 (API v3) to build this program. +*** This library is part of the GNU Taler exchange, available at +*** https://taler.net +*** ]])]) # check for libmicrohttpd diff --git a/src/challenger/challenger-httpd_authorize.c b/src/challenger/challenger-httpd_authorize.c @@ -1,6 +1,6 @@ /* This file is part of Challenger - Copyright (C) 2023 Taler Systems SA + Copyright (C) 2023, 2024 Taler Systems SA Challenger 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 @@ -26,6 +26,38 @@ #include "challenger-httpd_common.h" +/** + * Generate error reply in the format requested by + * the client. + * + * @param hc our context + * @param template error template to use + * @param http_status HTTP status to return + * @param ec error code to return + * @param hint human-readable hint to give + */ +static MHD_RESULT +reply_error (struct CH_HandlerContext *hc, + const char *template, + unsigned int http_status, + enum TALER_ErrorCode ec, + const char *hint) +{ + if (0 == CH_get_output_type (hc->connection)) + return TALER_TEMPLATING_reply_error ( + hc->connection, + template, + http_status, + ec, + hint); + return TALER_MHD_reply_with_error ( + hc->connection, + http_status, + ec, + hint); +} + + MHD_RESULT CH_handler_authorize (struct CH_HandlerContext *hc, const char *upload_data, @@ -47,10 +79,12 @@ CH_handler_authorize (struct CH_HandlerContext *hc, sizeof (nonce))) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_GENERIC_PARAMETER_MISSING, - hc->path); + return reply_error ( + hc, + "invalid-request", + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_PARAMETER_MISSING, + hc->path); } response_type = MHD_lookup_connection_value (hc->connection, @@ -59,19 +93,23 @@ CH_handler_authorize (struct CH_HandlerContext *hc, if (NULL == response_type) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "response_type"); + return reply_error ( + hc, + "invalid-request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + "response_type"); } if (0 != strcmp (response_type, "code")) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "response_type (must be 'code')"); + return reply_error ( + hc, + "invalid-request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "response_type (must be 'code')"); } { @@ -85,10 +123,12 @@ CH_handler_authorize (struct CH_HandlerContext *hc, if (NULL == client_id_str) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "client_id"); + return reply_error ( + hc, + "invalid_request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + "client_id"); } if (1 != sscanf (client_id_str, "%llu%c", @@ -96,10 +136,12 @@ CH_handler_authorize (struct CH_HandlerContext *hc, &dummy)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "client_id"); + return reply_error ( + hc, + "invalid-request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "client_id"); } } redirect_uri @@ -120,10 +162,12 @@ CH_handler_authorize (struct CH_HandlerContext *hc, strlen ("https://"))) ) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "redirect_uri (has to start with 'http://' or 'https://')"); + return reply_error ( + hc, + "invalid-request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "redirect_uri (has to start with 'http://' or 'https://')"); } state = MHD_lookup_connection_value (hc->connection, @@ -131,6 +175,7 @@ CH_handler_authorize (struct CH_HandlerContext *hc, "state"); if (NULL == state) state = ""; + scope = MHD_lookup_connection_value (hc->connection, MHD_GET_ARGUMENT_KIND, @@ -158,11 +203,12 @@ CH_handler_authorize (struct CH_HandlerContext *hc, { case GNUNET_DB_STATUS_HARD_ERROR: GNUNET_break (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "internal-error", - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "authorize_start"); + return reply_error ( + hc, + "internal-error", + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "authorize_start"); case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); return MHD_NO; @@ -172,11 +218,12 @@ CH_handler_authorize (struct CH_HandlerContext *hc, "Failed to find authorization process of client %llu for nonce `%s'\n", client_id, hc->path); - return TALER_TEMPLATING_reply_error (hc->connection, - "validation-unknown", - MHD_HTTP_NOT_FOUND, - TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, - NULL); + return reply_error ( + hc, + "validation-unknown", + MHD_HTTP_NOT_FOUND, + TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, + NULL); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } @@ -221,22 +268,32 @@ CH_handler_authorize (struct CH_HandlerContext *hc, address_attempts_left) ); http_status = MHD_HTTP_OK; - ret = TALER_TEMPLATING_build ( - hc->connection, - &http_status, - form, - NULL, - NULL, - args, - &resp); - GNUNET_free (form); - json_decref (args); - if (GNUNET_SYSERR == ret) + if (0 == CH_get_output_type (hc->connection)) + { + ret = TALER_TEMPLATING_build ( + hc->connection, + &http_status, + form, + NULL, + NULL, + args, + &resp); + GNUNET_free (form); + json_decref (args); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + return MHD_NO; + } + GNUNET_break (GNUNET_OK == ret); + } + else { - GNUNET_break (0); - return MHD_NO; + GNUNET_break (0 == + json_object_del (args, + "nonce")); + resp = TALER_MHD_make_json_steal (args); } - GNUNET_break (GNUNET_OK == ret); GNUNET_break (MHD_YES == MHD_add_response_header (resp, MHD_HTTP_HEADER_CACHE_CONTROL, diff --git a/src/challenger/challenger-httpd_challenge.c b/src/challenger/challenger-httpd_challenge.c @@ -1,6 +1,6 @@ /* This file is part of Challenger - Copyright (C) 2023 Taler Systems SA + Copyright (C) 2023, 2024 Taler Systems SA Challenger 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 @@ -224,6 +224,40 @@ cleanup_ctx (void *cls) /** + * Generate error reply in the format requested by + * the client. + * + * @param bc our context + * @param template error template to use + * @param http_status HTTP status to return + * @param ec error code to return + * @param hint human-readable hint to give + */ +static MHD_RESULT +reply_error (struct ChallengeContext *bc, + const char *template, + unsigned int http_status, + enum TALER_ErrorCode ec, + const char *hint) +{ + struct CH_HandlerContext *hc = bc->hc; + + if (0 == CH_get_output_type (hc->connection)) + return TALER_TEMPLATING_reply_error ( + hc->connection, + template, + http_status, + ec, + hint); + return TALER_MHD_reply_with_error ( + hc->connection, + http_status, + ec, + hint); +} + + +/** * Function called when our PIN transmission helper has terminated. * * @param cls our `struct ChallengeContext *` @@ -270,11 +304,11 @@ send_tan (struct ChallengeContext *bc) MHD_RESULT mres; GNUNET_break (0); - mres = TALER_TEMPLATING_reply_error (bc->hc->connection, - "internal-error", - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, - "pipe"); + mres = reply_error (bc, + "internal-error", + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, + "pipe"); bc->status = (MHD_YES == mres) ? GNUNET_NO : GNUNET_SYSERR; @@ -305,11 +339,11 @@ send_tan (struct ChallengeContext *bc) GNUNET_break (0); GNUNET_DISK_pipe_close (p); - mres = TALER_TEMPLATING_reply_error (bc->hc->connection, - "internal-error", - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, - "exec"); + mres = reply_error (bc, + "internal-error", + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, + "exec"); bc->status = (MHD_YES == mres) ? GNUNET_NO : GNUNET_SYSERR; @@ -339,11 +373,11 @@ send_tan (struct ChallengeContext *bc) MHD_RESULT mres; GNUNET_break (0); - mres = TALER_TEMPLATING_reply_error (bc->hc->connection, - "internal-error", - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, - "write"); + mres = reply_error (bc, + "internal-error", + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, + "write"); GNUNET_free (msg); bc->status = (MHD_YES == mres) ? GNUNET_NO @@ -516,11 +550,11 @@ CH_handler_challenge (struct CH_HandlerContext *hc, sizeof (bc->nonce))) { GNUNET_break_op (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "invalid-request", - MHD_HTTP_NOT_FOUND, - TALER_EC_GENERIC_PARAMETER_MISSING, - hc->path); + return reply_error (bc, + "invalid-request", + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_PARAMETER_MISSING, + hc->path); } TALER_MHD_check_content_length (hc->connection, 1024); @@ -548,11 +582,11 @@ CH_handler_challenge (struct CH_HandlerContext *hc, "%u/%d", (unsigned int) bc->exit_code, bc->pst); - return TALER_TEMPLATING_reply_error (hc->connection, - "internal-error", - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, - es); + return reply_error (bc, + "internal-error", + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, + es); } /* handle upload */ if (0 != *upload_data_size) @@ -596,11 +630,11 @@ CH_handler_challenge (struct CH_HandlerContext *hc, if (NULL != bad_field) { GNUNET_break_op (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "invalid-request", - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - bad_field); + return reply_error (bc, + "invalid-request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + bad_field); } } if (! bc->db_finished) @@ -624,21 +658,21 @@ CH_handler_challenge (struct CH_HandlerContext *hc, { case GNUNET_DB_STATUS_HARD_ERROR: GNUNET_break (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "internal-error", - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "set-address-and-pin"); + return reply_error (bc, + "internal-error", + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "set-address-and-pin"); case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); return MHD_NO; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: GNUNET_break_op (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "validation-unknown", - MHD_HTTP_NOT_FOUND, - TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, - NULL); + return reply_error (bc, + "validation-unknown", + MHD_HTTP_NOT_FOUND, + TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, + NULL); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } @@ -707,20 +741,30 @@ CH_handler_challenge (struct CH_HandlerContext *hc, CH_validation_duration))) ); http_status = MHD_HTTP_OK; - ret = TALER_TEMPLATING_build (hc->connection, - &http_status, - "enter-tan-form", - NULL, - NULL, - args, - &resp); - json_decref (args); - if (GNUNET_SYSERR == ret) + if (0 == CH_get_output_type (hc->connection)) { - GNUNET_break (0); - return MHD_NO; + ret = TALER_TEMPLATING_build (hc->connection, + &http_status, + "enter-tan-form", + NULL, + NULL, + args, + &resp); + json_decref (args); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + return MHD_NO; + } + GNUNET_break (GNUNET_OK == ret); + } + else + { + GNUNET_break (0 == + json_object_del (args, + "nonce")); + resp = TALER_MHD_make_json (args); } - GNUNET_break (GNUNET_OK == ret); GNUNET_break (MHD_YES == MHD_add_response_header (resp, MHD_HTTP_HEADER_CACHE_CONTROL, diff --git a/src/challenger/challenger-httpd_common.c b/src/challenger/challenger-httpd_common.c @@ -24,6 +24,25 @@ #define RFC_8959_PREFIX "secret-token:" + +int +CH_get_output_type (struct MHD_Connection *connection) +{ + int output_type; + + output_type + = TALER_MHD_check_accept (connection, + MHD_HTTP_HEADER_ACCEPT, + "text/html", + "application/json", + NULL); + if (output_type < 0) + output_type = 0; /* default to HTML */ + GNUNET_assert (output_type < 2); + return output_type; +} + + const char * CH_get_client_secret (struct MHD_Connection *connection) { diff --git a/src/challenger/challenger-httpd_common.h b/src/challenger/challenger-httpd_common.h @@ -35,6 +35,16 @@ CH_get_client_secret (struct MHD_Connection *connection); /** + * Return desired output format. + * + * @param connection connection to check + * @return 0 for HTML and 1 for JSON. + */ +int +CH_get_output_type (struct MHD_Connection *connection); + + +/** * Compute code that would grant access to the ``/token`` * endpoint to obtain an access token for a particular * challenge address. NOTE: We may not want diff --git a/src/challenger/challenger-httpd_config.c b/src/challenger/challenger-httpd_config.c @@ -1,6 +1,6 @@ /* This file is part of Challenger - Copyright (C) 2023 Taler Systems SA + Copyright (C) 2023, 2024 Taler Systems SA Challenger is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -27,6 +27,7 @@ * Protocol version history: * * 0: original design + * 1: revision to support SPA */ @@ -45,7 +46,7 @@ CH_handler_config (struct CH_HandlerContext *hc, GNUNET_JSON_pack_string ("name", "challenger"), GNUNET_JSON_pack_string ("version", - "0:1:0")); + "1:0:1")); } diff --git a/src/challenger/challenger-httpd_solve.c b/src/challenger/challenger-httpd_solve.c @@ -1,6 +1,6 @@ /* This file is part of Challenger - Copyright (C) 2023 Taler Systems SA + Copyright (C) 2023, 2024 Taler Systems SA Challenger 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 @@ -41,6 +41,11 @@ struct SolveContext struct CHALLENGER_ValidationNonceP nonce; /** + * HTTP request context. + */ + struct CH_HandlerContext *hc; + + /** * Handle for processing uploaded data. */ struct MHD_PostProcessor *pp; @@ -106,6 +111,40 @@ cleanup_ctx (void *cls) /** + * Generate error reply in the format requested by + * the client. + * + * @param bc our context + * @param template error template to use + * @param http_status HTTP status to return + * @param ec error code to return + * @param hint human-readable hint to give + */ +static MHD_RESULT +reply_error (struct SolveContext *bc, + const char *template, + unsigned int http_status, + enum TALER_ErrorCode ec, + const char *hint) +{ + struct CH_HandlerContext *hc = bc->hc; + + if (0 == CH_get_output_type (hc->connection)) + return TALER_TEMPLATING_reply_error ( + hc->connection, + template, + http_status, + ec, + hint); + return TALER_MHD_reply_with_error ( + hc->connection, + http_status, + ec, + hint); +} + + +/** * Iterator over key-value pairs where the value may be made available * in increments and/or may not be zero-terminated. Used for * processing POST data. @@ -169,6 +208,7 @@ CH_handler_solve (struct CH_HandlerContext *hc, bc = GNUNET_new (struct SolveContext); hc->cc = &cleanup_ctx; hc->ctx = bc; + bc->hc = hc; bc->pp = MHD_create_post_processor (hc->connection, 1024, &post_iter, @@ -180,11 +220,11 @@ CH_handler_solve (struct CH_HandlerContext *hc, sizeof (bc->nonce))) { GNUNET_break_op (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "invalid-request", - MHD_HTTP_BAD_REQUEST, - TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, - hc->path); + return reply_error (bc, + "invalid-request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, + hc->path); } TALER_MHD_check_content_length (hc->connection, 1024); @@ -206,11 +246,11 @@ CH_handler_solve (struct CH_HandlerContext *hc, if (NULL == bc->pin) { GNUNET_break_op (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "invalid-request", - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "pin"); + return reply_error (bc, + "invalid-request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + "pin"); } { unsigned int pin; @@ -226,11 +266,11 @@ CH_handler_solve (struct CH_HandlerContext *hc, &dummy)) { GNUNET_break_op (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "invalid-request", - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "pin"); + return reply_error (bc, + "invalid-request", + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "pin"); } qs = CH_db->validate_solve_pin (CH_db->cls, @@ -247,8 +287,8 @@ CH_handler_solve (struct CH_HandlerContext *hc, switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: - return TALER_TEMPLATING_reply_error ( - hc->connection, + return reply_error ( + bc, "internal-error", MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, @@ -257,8 +297,8 @@ CH_handler_solve (struct CH_HandlerContext *hc, GNUNET_break (0); return MHD_NO; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_TEMPLATING_reply_error ( - hc->connection, + return reply_error ( + bc, "validation-unknown", MHD_HTTP_NOT_FOUND, TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, @@ -287,28 +327,31 @@ CH_handler_solve (struct CH_HandlerContext *hc, } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Invalid PIN supplied, client has chance to solve it again\n"); + "Invalid PIN supplied\n"); details = GNUNET_JSON_PACK ( TALER_JSON_pack_ec (TALER_EC_CHALLENGER_INVALID_PIN), - GNUNET_JSON_pack_bool ("exhausted", - exhausted), - GNUNET_JSON_pack_bool ("no_challenge", - no_challenge), - GNUNET_JSON_pack_uint64 ("addresses_left", - bc->addr_left), GNUNET_JSON_pack_uint64 ("addresses_left", bc->addr_left), GNUNET_JSON_pack_uint64 ("pin_transmissions_left", bc->pin_transmissions_left), GNUNET_JSON_pack_uint64 ("auth_attempts_left", - bc->auth_attempts_left) + bc->auth_attempts_left), + GNUNET_JSON_pack_bool ("exhausted", + exhausted), + GNUNET_JSON_pack_bool ("no_challenge", + no_challenge) ); - ret = TALER_TEMPLATING_reply (hc->connection, - MHD_HTTP_FORBIDDEN, - "invalid-pin", - NULL, - NULL, - details); + if (0 == CH_get_output_type (hc->connection)) + ret = TALER_TEMPLATING_reply (hc->connection, + MHD_HTTP_FORBIDDEN, + "invalid-pin", + NULL, + NULL, + details); + else + ret = TALER_MHD_reply_json (hc->connection, + details, + MHD_HTTP_FORBIDDEN); json_decref (details); return ret; } @@ -337,20 +380,20 @@ CH_handler_solve (struct CH_HandlerContext *hc, { case GNUNET_DB_STATUS_HARD_ERROR: GNUNET_break (0); - return TALER_TEMPLATING_reply_error (hc->connection, - "internal-server-error", - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "validation_get"); + return reply_error (bc, + "internal-server-error", + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "validation_get"); case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); return MHD_NO; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_TEMPLATING_reply_error (hc->connection, - "validation-unknown", - MHD_HTTP_NOT_FOUND, - TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, - NULL); + return reply_error (bc, + "validation-unknown", + MHD_HTTP_NOT_FOUND, + TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, + NULL); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; }