/* This file is part of TALER Copyright (C) 2024 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 */ /** * @file lib/donau_api_batch_issue_receipts.c * @brief Implementation of the "handle" component of the donau's HTTP API * @author Lukas Matyja */ #include #include #include #include "donau_service.h" #include "donau_util.h" #include "donau_api_curl_defaults.h" #include "donau_json_lib.h" /** * Handle for a POST /batch-issue/$CHARITY_ID request. */ struct DONAU_BatchIssueReceiptHandle { /** * The url for the /batch-issue/$CHARITY_ID request. */ char *url; /** * Minor context that holds body and headers. */ struct TALER_CURL_PostContext post_ctx; /** * Entry for this request with the `struct GNUNET_CURL_Context`. */ struct GNUNET_CURL_Job *job; /** * Function to call with the result. */ DONAU_BatchIssueReceiptsCallback cb; /** * BUDI-key-pair signature. */ struct DONAU_CharitySignatureP charity_sig; /** * Closure to pass to @e cb. */ void *cb_cls; /** * Reference to the execution context. */ struct GNUNET_CURL_Context *ctx; }; /** * Transform issue receipt request into JSON. * * @param num_bkp number of budi-key-pairs in @bkp * @param bkp budi-key-pair array * @param year corresponding year * @param charity_sig signature from charity over @bkp */ json_t * issue_receipt_body_to_json (const unsigned int num_bkp, const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp, const uint64_t year, const struct DONAU_CharitySignatureP *charity_sig) { json_t *budikeypairs = json_array (); GNUNET_assert (NULL != budikeypairs); for (size_t i = 0; i < num_bkp; i++) { json_t *budikeypair = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("h_du_pub", &bkp[i].h_donation_unit_pub.hash), DONAU_JSON_pack_blinded_donation_identifier ("blinded_udi", &bkp[i].blinded_udi)); GNUNET_assert (0 == json_array_append_new (budikeypairs, budikeypair)); } return GNUNET_JSON_PACK ( GNUNET_JSON_pack_array_steal ("budikeypairs", budikeypairs), GNUNET_JSON_pack_data_auto ("charity_sig", &charity_sig->eddsa_sig), GNUNET_JSON_pack_uint64 ("year", year)); } /** * Function called when we're done processing the * HTTP POST /batch-issue/$CHARITY_ID request. * * @param cls the `struct KeysRequest` * @param response_code HTTP response code, 0 on error * @param resp_obj parsed JSON result, NULL on error */ static void handle_batch_issue_finished (void *cls, long response_code, const void *resp_obj) { struct DONAU_BatchIssueReceiptHandle *birh = cls; const json_t *j = resp_obj; struct DONAU_BatchIssueResponse biresp = { .hr.reply = j, .hr.http_status = (unsigned int) response_code }; birh->job = NULL; switch (response_code) { case MHD_HTTP_CREATED: break; case MHD_HTTP_NO_CONTENT: biresp.hr.ec = TALER_JSON_get_error_code (j); biresp.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_FORBIDDEN: biresp.hr.ec = TALER_JSON_get_error_code (j); biresp.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_NOT_FOUND: biresp.hr.ec = TALER_JSON_get_error_code (j); biresp.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_CONTENT_TOO_LARGE: biresp.hr.ec = TALER_JSON_get_error_code (j); biresp.hr.hint = TALER_JSON_get_error_hint (j); break; default: /* unexpected response code */ GNUNET_break_op (0); biresp.hr.ec = TALER_JSON_get_error_code (j); biresp.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d for POST %s\n", (unsigned int) response_code, (int) biresp.hr.ec, birh->url); break; } if (NULL != birh->cb) { birh->cb (birh->cb_cls, &biresp); birh->cb = NULL; } DONAU_charity_issue_receipt_cancel (birh); } struct DONAU_BatchIssueReceiptHandle * DONAU_charity_issue_receipt ( struct GNUNET_CURL_Context *ctx, const char *url, const struct DONAU_CharityPrivateKeyP *charity_priv, const struct DONAU_CharityPublicKeyP *charity_pub, const uint64_t charity_id, const uint64_t year, const size_t num_bkp, const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp, DONAU_BatchIssueReceiptsCallback cb, void *cb_cls) { struct DONAU_BatchIssueReceiptHandle *birh; birh = GNUNET_new (struct DONAU_BatchIssueReceiptHandle); CURL *eh; json_t *body; // make signature over budi-key-pair DONAU_charity_bkp_sign (num_bkp, bkp, charity_priv, &birh->charity_sig); /* FIXME temporary test */ if (GNUNET_OK != DONAU_charity_bkp_verify (num_bkp, bkp, charity_pub, &birh->charity_sig)) { GNUNET_break_op (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "signature or verification function(s) not ok\n"); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "signature and verification functions ok\n"); TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", url); birh->url = GNUNET_strdup (url); birh->cb = cb; birh->cb_cls = cb_cls; birh->ctx = ctx; char arg_str[sizeof (charity_id) * 2 + 32]; GNUNET_snprintf (arg_str, sizeof (arg_str), "batch-issue/%llu", (unsigned long long) charity_id); birh->url = TALER_url_join (url, arg_str, NULL); if (NULL == birh->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not construct request URL.\n"); GNUNET_free (birh); return NULL; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "issue_receipts_with_URL `%s'.\n", birh->url); body = issue_receipt_body_to_json (num_bkp, bkp, year, &birh->charity_sig); eh = DONAU_curl_easy_get_ (birh->url); if ( (NULL == eh) || (GNUNET_OK != TALER_curl_easy_post (&birh->post_ctx, eh, body)) ) { GNUNET_break (0); if (NULL != eh) curl_easy_cleanup (eh); json_decref (body); GNUNET_free (birh->url); return NULL; } json_decref (body); birh->job = GNUNET_CURL_job_add2 (ctx, eh, birh->post_ctx.headers, &handle_batch_issue_finished, birh); return birh; } void DONAU_charity_issue_receipt_cancel ( struct DONAU_BatchIssueReceiptHandle *birh) { if (NULL != birh->job) { GNUNET_CURL_job_cancel (birh->job); birh->job = NULL; } TALER_curl_easy_post_finished (&birh->post_ctx); GNUNET_free (birh->url); GNUNET_free (birh); }