/*
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);
}