From 81311476b804c054e4ee19c9b182f3b34357f88f Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 1 Mar 2022 23:35:42 +0100 Subject: -first rough cut client implementation of the new /truth/ APIs --- doc/sphinx/rest.rst | 8 + src/include/anastasis_service.h | 21 +- src/restclient/Makefile.am | 4 +- src/restclient/anastasis_api_truth_challenge.c | 420 ++++++++++++++++++++++++ src/restclient/anastasis_api_truth_solve.c | 437 +++++++++++++++++++++++++ 5 files changed, 887 insertions(+), 3 deletions(-) create mode 100644 src/restclient/anastasis_api_truth_challenge.c create mode 100644 src/restclient/anastasis_api_truth_solve.c diff --git a/doc/sphinx/rest.rst b/doc/sphinx/rest.rst index 18b389e..206eda7 100644 --- a/doc/sphinx/rest.rst +++ b/doc/sphinx/rest.rst @@ -674,6 +674,10 @@ charge per truth operation using GNU Taler. // the according `EscrowMethod`_. The server needs this key to get the // info out of `TruthUploadRequest`_ to verify the ``h_response``. truth_decryption_key: ANASTASIS_TruthKeyP; + + // Reference to a payment made by the client to + // pay for this request. Optional. + payment_secret?: ANASTASIS_PaymentSecretP; } @@ -774,6 +778,10 @@ charge per truth operation using GNU Taler. // the according `EscrowMethod`_. The server needs this key to get the // info out of `TruthUploadRequest`_ to verify the ``h_response``. truth_decryption_key: ANASTASIS_TruthKeyP; + + // Reference to a payment made by the client to + // pay for this request. Optional. + payment_secret?: ANASTASIS_PaymentSecretP; } diff --git a/src/include/anastasis_service.h b/src/include/anastasis_service.h index dc0fb12..c21cde5 100644 --- a/src/include/anastasis_service.h +++ b/src/include/anastasis_service.h @@ -865,6 +865,11 @@ struct ANASTASIS_TruthChallengeDetails */ enum TALER_ErrorCode ec; + /** + * Full response in JSON, if provided. + */ + const json_t *response; + /** * Details depending on @e http_status. */ @@ -944,6 +949,12 @@ struct ANASTASIS_TruthChallengeDetails * The payment secret (aka order ID) extracted from the @e payment_request. */ struct ANASTASIS_PaymentSecretP ps; + + /** + * Data extracted from the payto:// URI. + */ + const struct TALER_MERCHANT_PayUriData *pd; + } payment_required; } details; @@ -988,7 +999,7 @@ ANASTASIS_truth_challenge ( const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, const struct ANASTASIS_CRYPTO_TruthKeyP *truth_key, const struct ANASTASIS_PaymentSecretP *payment_secret, - ANASTASIS_KeyShareLookupCallback cb, + ANASTASIS_TruthChallengeCallback cb, void *cb_cls); @@ -1052,6 +1063,12 @@ struct ANASTASIS_TruthSolveReply * The payment secret (aka order ID) extracted from the @e payment_request. */ struct ANASTASIS_PaymentSecretP ps; + + /** + * Data extracted from the payto:// URI. + */ + const struct TALER_MERCHANT_PayUriData *pd; + } payment_required; /** @@ -1119,7 +1136,7 @@ ANASTASIS_truth_solve ( const struct ANASTASIS_PaymentSecretP *payment_secret, struct GNUNET_TIME_Relative timeout, const struct GNUNET_HashCode *hashed_answer, - ANASTASIS_KeyShareLookupCallback cb, + ANASTASIS_TruthSolveCallback cb, void *cb_cls); diff --git a/src/restclient/Makefile.am b/src/restclient/Makefile.am index 19bf64c..b2e9d0b 100644 --- a/src/restclient/Makefile.am +++ b/src/restclient/Makefile.am @@ -17,9 +17,11 @@ libanastasisrest_la_LDFLAGS = \ libanastasisrest_la_SOURCES = \ anastasis_api_config.c \ anastasis_api_policy_store.c \ - anastasis_api_truth_store.c \ anastasis_api_policy_lookup.c \ anastasis_api_policy_meta_lookup.c \ + anastasis_api_truth_challenge.c \ + anastasis_api_truth_solve.c \ + anastasis_api_truth_store.c \ anastasis_api_keyshare_lookup.c \ anastasis_api_curl_defaults.c anastasis_api_curl_defaults.h libanastasisrest_la_LIBADD = \ diff --git a/src/restclient/anastasis_api_truth_challenge.c b/src/restclient/anastasis_api_truth_challenge.c new file mode 100644 index 0000000..92916d6 --- /dev/null +++ b/src/restclient/anastasis_api_truth_challenge.c @@ -0,0 +1,420 @@ +/* + This file is part of Anastasis + Copyright (C) 2020, 2021, 2022 Anastasis SARL + + Anastasis 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. + + Anastasis 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 + Anastasis; see the file COPYING.GPL. If not, see +*/ +/** + * @file restclient/anastasis_api_truth_challenge.c + * @brief Implementation of the POST /truth/$TID/challenge request on the client-side + * @author Christian Grothoff + * @author Dennis Neufeld + * @author Dominik Meister + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include "anastasis_service.h" +#include "anastasis_api_curl_defaults.h" +#include +#include +#include + + +/** + * @brief A Contract Operation Handle + */ +struct ANASTASIS_TruthChallengeOperation +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + ANASTASIS_TruthChallengeCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * Payment URI we received from the service, or NULL. + */ + char *pay_uri; + + /** + * Content type of the body. + */ + char *content_type; +}; + + +void +ANASTASIS_truth_challenge_cancel ( + struct ANASTASIS_TruthChallengeOperation *tco) +{ + if (NULL != tco->job) + { + GNUNET_CURL_job_cancel (tco->job); + tco->job = NULL; + } + GNUNET_free (tco->pay_uri); + GNUNET_free (tco->url); + GNUNET_free (tco->content_type); + TALER_curl_easy_post_finished (&tco->ctx); + GNUNET_free (tco); +} + + +/** + * Process POST /truth/$TID/challenge response + * + * @param cls our `struct ANASTASIS_TruthChallengeOperation *` + * @param response_code the HTTP status + * @param response parsed JSON result, NULL one rrro + */ +static void +handle_truth_challenge_finished (void *cls, + long response_code, + const void *response) +{ + struct ANASTASIS_TruthChallengeOperation *tco = cls; + const json_t *j = response; + struct ANASTASIS_TruthChallengeDetails tcd = { + .http_status = response_code, + .response = j + }; + + tco->job = NULL; + switch (response_code) + { + case 0: + /* Hard error */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Backend didn't even return from POST /truth/$TID/challenge\n"); + tcd.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + const char *ct; + const char *tan_hint = NULL; + json_t *wire_details = NULL; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ( + "challenge_type", + &ct), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("tan_address_hint", + &tan_hint)), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("wire_details", + &wire_details)), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + tcd.http_status = 0; + tcd.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + if (0 == strcmp (ct, + "TAN_SENT")) + { + tcd.details.success.cs = ANASTASIS_CS_TAN_SENT; + tcd.details.success.details.tan_address_hint = tan_hint; + } + else if ( (0 == strcmp (ct, + "WIRE_FUNDS")) && + (NULL != wire_details) ) + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_string ( + "target_account", + &tcd.details.success.details.wire_funds.target_payto), + GNUNET_JSON_spec_string ( + "sender_hint", + &tcd.details.success.details.wire_funds.sender_hint), + TALER_JSON_spec_amount_any ("amount", + &tcd.details.success.details.wire_funds. + amount), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (wire_details, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + tcd.http_status = 0; + tcd.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + tcd.details.success.cs = ANASTASIS_CS_WIRE_FUNDS; + tco->cb (tco->cb_cls, + &tcd); + GNUNET_JSON_parse_free (spec); + ANASTASIS_truth_challenge_cancel (tco); + return; + } + } + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the anastasis server is buggy + (or API version conflict); just pass JSON reply to the application */ + GNUNET_break (0); + tcd.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_PAYMENT_REQUIRED: + { + struct TALER_MERCHANT_PayUriData pd; + + if ( (NULL == tco->pay_uri) || + (GNUNET_OK != + TALER_MERCHANT_parse_pay_uri (tco->pay_uri, + &pd)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse `%s'\n", + tco->pay_uri); + tcd.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data ( + pd.order_id, + strlen (pd.order_id), + &tcd.details.payment_required.ps, + sizeof (tcd.details.payment_required.ps))) + { + GNUNET_break (0); + tcd.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + TALER_MERCHANT_parse_pay_uri_free (&pd); + break; + } + tcd.details.payment_required.pd = &pd; + tcd.details.payment_required.payment_request = tco->pay_uri; + tco->cb (tco->cb_cls, + &tcd); + TALER_MERCHANT_parse_pay_uri_free (&pd); + ANASTASIS_truth_challenge_cancel (tco); + return; + } + break; + case MHD_HTTP_FORBIDDEN: + /* Nothing really to verify, authentication required/failed */ + tcd.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify */ + tcd.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + tcd.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_BAD_GATEWAY: + tcd.ec = TALER_JSON_get_error_code (j); + break; + default: + /* unexpected response code */ + tcd.ec = TALER_JSON_get_error_code (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d to POST /truth/$TID/challenge\n", + (unsigned int) response_code, + (int) tcd.ec); + GNUNET_break (0); + break; + } + tco->cb (tco->cb_cls, + &tcd); + ANASTASIS_truth_challenge_cancel (tco); +} + + +/** + * Patch value in @a val, replacing new line with '\0'. + * + * @param[in,out] val 0-terminated string to replace '\\n' and '\\r' with '\\0' in. + */ +static void +patch_value (char *val) +{ + size_t len; + + /* found location URI we care about! */ + len = strlen (val); + while ( (len > 0) && + ( ('\n' == val[len - 1]) || + ('\r' == val[len - 1]) ) ) + { + len--; + val[len] = '\0'; + } +} + + +/** + * Handle HTTP header received by curl. + * + * @param buffer one line of HTTP header data + * @param size size of an item + * @param nitems number of items passed + * @param userdata our `struct ANASTASIS_StorePolicyOperation *` + * @return `size * nitems` + */ +static size_t +handle_header (char *buffer, + size_t size, + size_t nitems, + void *userdata) +{ + struct ANASTASIS_TruthChallengeOperation *tco = userdata; + size_t total = size * nitems; + char *ndup; + const char *hdr_type; + char *hdr_val; + char *sp; + + ndup = GNUNET_strndup (buffer, + total); + hdr_type = strtok_r (ndup, + ":", + &sp); + if (NULL == hdr_type) + { + GNUNET_free (ndup); + return total; + } + hdr_val = strtok_r (NULL, + "", + &sp); + if (NULL == hdr_val) + { + GNUNET_free (ndup); + return total; + } + if (' ' == *hdr_val) + hdr_val++; + if (0 == strcasecmp (hdr_type, + ANASTASIS_HTTP_HEADER_TALER)) + { + /* found payment URI we care about! */ + GNUNET_free (tco->pay_uri); + tco->pay_uri = GNUNET_strdup (hdr_val); + patch_value (tco->pay_uri); + } + if (0 == strcasecmp (hdr_type, + MHD_HTTP_HEADER_CONTENT_TYPE)) + { + /* found location URI we care about! */ + GNUNET_free (tco->content_type); + tco->content_type = GNUNET_strdup (hdr_val); + patch_value (tco->content_type); + } + GNUNET_free (ndup); + return total; +} + + +struct ANASTASIS_TruthChallengeOperation * +ANASTASIS_truth_challenge ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_CRYPTO_TruthKeyP *truth_key, + const struct ANASTASIS_PaymentSecretP *payment_secret, + ANASTASIS_TruthChallengeCallback cb, + void *cb_cls) +{ + struct ANASTASIS_TruthChallengeOperation *tco; + CURL *eh; + json_t *body; + + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("truth_decryption_key", + truth_key), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_data_auto ("payment_secret", + payment_secret))); + GNUNET_assert (NULL != body); + tco = GNUNET_new (struct ANASTASIS_TruthChallengeOperation); + tco->cb = cb; + tco->cb_cls = cb_cls; + { + char *path; + char *uuid_str; + + uuid_str = GNUNET_STRINGS_data_to_string_alloc (truth_uuid, + sizeof (*truth_uuid)); + GNUNET_asprintf (&path, + "truth/%s/challenge", + uuid_str); + GNUNET_free (uuid_str); + tco->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + eh = ANASTASIS_curl_easy_get_ (tco->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&tco->ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (tco->url); + GNUNET_free (tco); + return NULL; + } + json_decref (body); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_HEADERFUNCTION, + &handle_header)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_HEADERDATA, + tco)); + tco->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_truth_challenge_finished, + tco); + return tco; +} + + +/* end of anastasis_api_truth_challenge.c */ diff --git a/src/restclient/anastasis_api_truth_solve.c b/src/restclient/anastasis_api_truth_solve.c new file mode 100644 index 0000000..376fc74 --- /dev/null +++ b/src/restclient/anastasis_api_truth_solve.c @@ -0,0 +1,437 @@ +/* + This file is part of Anastasis + Copyright (C) 2020, 2021, 2022 Anastasis SARL + + Anastasis 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. + + Anastasis 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 + Anastasis; see the file COPYING.GPL. If not, see +*/ +/** + * @file restclient/anastasis_api_truth_solve.c + * @brief Implementation of the POST /truth/$TID/solve request + * @author Christian Grothoff + * @author Dennis Neufeld + * @author Dominik Meister + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include "anastasis_service.h" +#include "anastasis_api_curl_defaults.h" +#include +#include +#include + + +/** + * @brief A Contract Operation Handle + */ +struct ANASTASIS_TruthSolveOperation +{ + /** + * The url for this request, including parameters. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + ANASTASIS_TruthSolveCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * Payment URI we received from the service, or NULL. + */ + char *pay_uri; + + /** + * Content type of the body. + */ + char *content_type; +}; + + +void +ANASTASIS_truth_solve_cancel ( + struct ANASTASIS_TruthSolveOperation *tso) +{ + if (NULL != tso->job) + { + GNUNET_CURL_job_cancel (tso->job); + tso->job = NULL; + } + GNUNET_free (tso->pay_uri); + GNUNET_free (tso->url); + GNUNET_free (tso->content_type); + TALER_curl_easy_post_finished (&tso->ctx); + GNUNET_free (tso); +} + + +/** + * Process POST /truth/$TID/solve response + * + * @param cls our `struct ANASTASIS_TruthSolveOperation *` + * @param response_code the HTTP status + * @param data the body of the response + * @param data_size number of bytes in @a data + */ +static void +handle_truth_solve_finished (void *cls, + long response_code, + const void *data, + size_t data_size) +{ + struct ANASTASIS_TruthSolveOperation *tso = cls; + struct ANASTASIS_TruthSolveReply tsr = { + .http_status = response_code + }; + + tso->job = NULL; + switch (response_code) + { + case 0: + /* Hard error */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Backend didn't even return from POST /truth/$TID/solve\n"); + tsr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (sizeof (tsr.details.success.eks) != data_size) + { + GNUNET_break_op (0); + tsr.http_status = 0; + tsr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + /* Success, call callback with all details! */ + memcpy (&tsr.details.success.eks, + data, + data_size); + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the anastasis server is buggy + (or API version conflict); just pass JSON reply to the application */ + GNUNET_break (0); + tsr.ec = TALER_JSON_get_error_code2 (data, + data_size); + break; + case MHD_HTTP_PAYMENT_REQUIRED: + { + struct TALER_MERCHANT_PayUriData pd; + + if ( (NULL == tso->pay_uri) || + (GNUNET_OK != + TALER_MERCHANT_parse_pay_uri (tso->pay_uri, + &pd)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse `%s'\n", + tso->pay_uri); + tsr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data ( + pd.order_id, + strlen (pd.order_id), + &tsr.details.payment_required.ps, + sizeof (tsr.details.payment_required.ps))) + { + GNUNET_break (0); + tsr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + TALER_MERCHANT_parse_pay_uri_free (&pd); + break; + } + tsr.details.payment_required.pd = &pd; + tsr.details.payment_required.payment_request = tso->pay_uri; + tso->cb (tso->cb_cls, + &tsr); + TALER_MERCHANT_parse_pay_uri_free (&pd); + ANASTASIS_truth_solve_cancel (tso); + return; + } + break; + case MHD_HTTP_FORBIDDEN: + tsr.ec = TALER_JSON_get_error_code2 (data, + data_size); + break; + case MHD_HTTP_NOT_FOUND: + tsr.ec = TALER_JSON_get_error_code2 (data, + data_size); + break; + case MHD_HTTP_REQUEST_TIMEOUT: + tsr.ec = TALER_JSON_get_error_code2 (data, + data_size); + break; + case MHD_HTTP_TOO_MANY_REQUESTS: + { + json_t *reply; + + reply = json_loadb (data, + data_size, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == reply) + { + GNUNET_break_op (0); + tsr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint32 ( + "request_limit", + &tsr.details.too_many_requests.request_limit), + GNUNET_JSON_spec_relative_time ( + "request_frequency", + &tsr.details.too_many_requests.request_frequency), + GNUNET_JSON_spec_end () + }; + if (GNUNET_OK != + GNUNET_JSON_parse (reply, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + tsr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + json_decref (reply); + break; + } + json_decref (reply); + break; + } + } + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + tsr.ec = TALER_JSON_get_error_code2 (data, + data_size); + break; + case MHD_HTTP_BAD_GATEWAY: + tsr.ec = TALER_JSON_get_error_code2 (data, + data_size); + break; + default: + /* unexpected response code */ + tsr.ec = TALER_JSON_get_error_code2 (data, + data_size); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d to POST /truth/$TID/solve\n", + (unsigned int) response_code, + (int) tsr.ec); + break; + } + tso->cb (tso->cb_cls, + &tsr); + ANASTASIS_truth_solve_cancel (tso); +} + + +/** + * Patch value in @a val, replacing new line with '\0'. + * + * @param[in,out] val 0-terminated string to replace '\\n' and '\\r' with '\\0' in. + */ +static void +patch_value (char *val) +{ + size_t len; + + /* found location URI we care about! */ + len = strlen (val); + while ( (len > 0) && + ( ('\n' == val[len - 1]) || + ('\r' == val[len - 1]) ) ) + { + len--; + val[len] = '\0'; + } +} + + +/** + * Handle HTTP header received by curl. + * + * @param buffer one line of HTTP header data + * @param size size of an item + * @param nitems number of items passed + * @param userdata our `struct ANASTASIS_StorePolicyOperation *` + * @return `size * nitems` + */ +static size_t +handle_header (char *buffer, + size_t size, + size_t nitems, + void *userdata) +{ + struct ANASTASIS_TruthSolveOperation *tso = userdata; + size_t total = size * nitems; + char *ndup; + const char *hdr_type; + char *hdr_val; + char *sp; + + ndup = GNUNET_strndup (buffer, + total); + hdr_type = strtok_r (ndup, + ":", + &sp); + if (NULL == hdr_type) + { + GNUNET_free (ndup); + return total; + } + hdr_val = strtok_r (NULL, + "", + &sp); + if (NULL == hdr_val) + { + GNUNET_free (ndup); + return total; + } + if (' ' == *hdr_val) + hdr_val++; + if (0 == strcasecmp (hdr_type, + ANASTASIS_HTTP_HEADER_TALER)) + { + /* found payment URI we care about! */ + GNUNET_free (tso->pay_uri); + tso->pay_uri = GNUNET_strdup (hdr_val); + patch_value (tso->pay_uri); + } + if (0 == strcasecmp (hdr_type, + MHD_HTTP_HEADER_CONTENT_TYPE)) + { + /* found location URI we care about! */ + GNUNET_free (tso->content_type); + tso->content_type = GNUNET_strdup (hdr_val); + patch_value (tso->content_type); + } + GNUNET_free (ndup); + return total; +} + + +struct ANASTASIS_TruthSolveOperation * +ANASTASIS_truth_solve ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_CRYPTO_TruthKeyP *truth_key, + const struct ANASTASIS_PaymentSecretP *payment_secret, + struct GNUNET_TIME_Relative timeout, + const struct GNUNET_HashCode *hashed_answer, + ANASTASIS_TruthSolveCallback cb, + void *cb_cls) +{ + struct ANASTASIS_TruthSolveOperation *tso; + CURL *eh; + char *path; + unsigned long long tms; + json_t *body; + + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("truth_decryption_key", + truth_key), + GNUNET_JSON_pack_data_auto ("h_response", + hashed_answer), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_data_auto ("payment_secret", + payment_secret))); + GNUNET_assert (NULL != body); + + tms = (unsigned long long) (timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); + tso = GNUNET_new (struct ANASTASIS_TruthSolveOperation); + tso->cb = cb; + tso->cb_cls = cb_cls; + { + char *uuid_str; + + uuid_str = GNUNET_STRINGS_data_to_string_alloc (truth_uuid, + sizeof (*truth_uuid)); + GNUNET_asprintf (&path, + "truth/%s/solve", + uuid_str); + GNUNET_free (uuid_str); + } + { + char timeout_ms[32]; + + GNUNET_snprintf (timeout_ms, + sizeof (timeout_ms), + "%llu", + tms); + tso->url = TALER_url_join (backend_url, + path, + "timeout_ms", + (! GNUNET_TIME_relative_is_zero (timeout)) + ? timeout_ms + : NULL, + NULL); + } + GNUNET_free (path); + eh = ANASTASIS_curl_easy_get_ (tso->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&tso->ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (tso->url); + GNUNET_free (tso); + return NULL; + } + json_decref (body); + if (0 != tms) + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 5000))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_HEADERFUNCTION, + &handle_header)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_HEADERDATA, + tso)); + tso->job = GNUNET_CURL_job_add_raw (ctx, + eh, + NULL, + &handle_truth_solve_finished, + tso); + return tso; +} + + +/* end of anastasis_api_truth_solve.c */ -- cgit v1.2.3