donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit 96acfb7bb41e3ee3c660e3228ab713fc3ddb79d8
parent 7ef32ba084d648e435585517efa4f6b0b1ee89b8
Author: Matyja Lukas Adam <lukas.matyja@students.bfh.ch>
Date:   Tue,  5 Mar 2024 00:31:33 +0100

[lib] add charity post

Diffstat:
Msrc/include/donau_service.h | 28++++++++++++++++++++++------
Msrc/lib/Makefile.am | 1+
Asrc/lib/donau_api_charity_delete.c | 258+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/donau_api_charity_post.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 545 insertions(+), 6 deletions(-)

diff --git a/src/include/donau_service.h b/src/include/donau_service.h @@ -870,7 +870,7 @@ typedef void * * @param ctx curl context * @param url donau base URL - * @param bearer for authentication + * @param bearer for authorization * @param cb the callback to call when a reply for this request is available * @param cb_cls closure for the above callback * @return a handle for this request; NULL if the inputs are invalid (i.e. @@ -997,7 +997,7 @@ typedef void * * @param ctx curl context * @param url donau base URL - * @param bearer for authentication + * @param bearer for authorization * @param id of the requested charity * @param cb the callback to call when a reply for this request is available * @param cb_cls closure for the above callback @@ -1038,15 +1038,31 @@ struct DONAU_CharityRequest char *name; /** + * URL + */ + char *charity_url; + + /** + * max donation amount per year + */ + struct TALER_Amount max_per_year; + + /** * max donation amount per year */ - struct TALER_Amount amount; + struct TALER_Amount receipts_to_date; /** * public key of the charity */ struct DONAU_DonauPublicKeyP charity_pub; + + /** + * current year + */ + uint64_t current_year; + }; /** * @brief A /charities Post Handle @@ -1115,7 +1131,7 @@ typedef void * @param ctx curl context * @param url donau base URL * @param charity_req contains the name, public key and the max donation amount - * @param bearer for authentication + * @param bearer for authorization * @param cb the callback to call when a reply for this request is available * @param cb_cls closure for the above callback * @return a handle for this request; NULL if the inputs are invalid (i.e. @@ -1125,7 +1141,7 @@ struct DONAU_CharityPostHandle * DONAU_charity_post ( struct GNUNET_CURL_Context *ctx, const char *url, - const struct DONAU_CharityRequest *charity_req, + struct DONAU_CharityRequest *charity_req, const struct DONAU_BearerToken bearer, DONAU_PostCharityResponseCallback cb, void *cb_cls); @@ -1259,7 +1275,7 @@ typedef void * @param ctx curl context * @param url donau base URL * @param id of the charity - * @param bearer for authentication + * @param bearer for authorization * @param cb the callback to call when a reply for this request is available * @param cb_cls closure for the above callback * @return a handle for this request; NULL if the inputs are invalid (i.e. diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -22,6 +22,7 @@ libdonau_la_LDFLAGS = \ libdonau_la_SOURCES = \ donau_api_handle.c \ donau_api_charity_get.c \ + donau_api_charity_post.c \ donau_api_charities_get.c \ donau_api_curl_defaults.c donau_api_curl_defaults.h diff --git a/src/lib/donau_api_charity_delete.c b/src/lib/donau_api_charity_delete.c @@ -0,0 +1,258 @@ +/* + 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 + <http://www.gnu.org/licenses/> +*/ + +/** + * @file lib/donau_api_charity_get.c + * @brief Implementation of the "handle" component of the donau's HTTP API + * @author Lukas Matyja + */ +#include <gnunet/gnunet_curl_lib.h> +#include <taler/taler_json_lib.h> +#include "donau_service.h" +#include "donau_api_curl_defaults.h" +#include "donau_json_lib.h" + + +/** + * Handle for a GET /charities/$CHARITY_ID request. + */ +struct DONAU_CharityGetHandle +{ + /** + * The url for the /charities/$CHARITY_ID request. + */ + char *url; + + /** + * Entry for this request with the `struct GNUNET_CURL_Context`. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + DONAU_GetCharityResponseCallback cb; + + /** + * Charity id we are querying. + */ + unsigned long long charity_id; + + /** + * Closure to pass to @e cb. + */ + void *cb_cls; + +}; + +/** + * Decode the JSON in @a resp_obj from the /charities/$ID response + * and store the data in the @a charity_data. + * + * @param[in] resp_obj JSON object to parse + * @param[out] charity_data where to store the results we decoded + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * (malformed JSON) + */ +static enum GNUNET_GenericReturnValue +handle_charity_get_ok (const json_t *resp_obj, + struct DONAU_CharityGetHandle *cgh, + struct DONAU_GetCharityResponse *gcresp) +{ + struct DONAU_Charity *charity = &gcresp->details.ok.charity; + const char *name; + if (JSON_OBJECT != json_typeof (resp_obj)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("charity_pub", + &charity->charity_pub), + GNUNET_JSON_spec_string ("name", &name), + TALER_JSON_spec_amount_any ("max_per_year", + &charity->max_per_year), + TALER_JSON_spec_amount_any ("receipts_to_date", + &charity-> + receipts_to_date), + GNUNET_JSON_spec_uint64 ("current_year", + &charity->current_year), + GNUNET_JSON_spec_end () + }; + if (GNUNET_OK != + GNUNET_JSON_parse (resp_obj, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + charity->name = GNUNET_strdup (name); + + cgh->cb (cgh->cb_cls, + gcresp); + cgh->cb = NULL; + return GNUNET_OK; +} + + +/** + * Callback used when downloading the reply to a /charity request + * is complete. + * + * @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_charity_get_finished (void *cls, + long response_code, + const void *resp_obj) +{ + // struct DONAU_Charity *cd = NULL; + + struct DONAU_CharityGetHandle *cgh = cls; + const json_t *j = resp_obj; + struct DONAU_GetCharityResponse gcresp = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + cgh->job = NULL; + switch (response_code) + { + case 0: + gcresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + handle_charity_get_ok (j, + cgh, + &gcresp)) + { + gcresp.hr.http_status = 0; + gcresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the donau is buggy + (or API version conflict); just pass JSON reply to the application */ + gcresp.hr.ec = TALER_JSON_get_error_code (j); + gcresp.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 */ + gcresp.hr.ec = TALER_JSON_get_error_code (j); + gcresp.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 */ + gcresp.hr.ec = TALER_JSON_get_error_code (j); + gcresp.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + gcresp.hr.ec = TALER_JSON_get_error_code (j); + gcresp.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for GET %s\n", + (unsigned int) response_code, + (int) gcresp.hr.ec, + cgh->url); + break; + } + if (NULL != cgh->cb) + { + cgh->cb (cgh->cb_cls, + &gcresp); + cgh->cb = NULL; + } + DONAU_charity_get_cancel (cgh); +} + + +struct DONAU_CharityGetHandle * +DONAU_charity_get ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const uint64_t id, + const struct DONAU_BearerToken bearer, // TODO: check authorization + DONAU_GetCharityResponseCallback cb, + void *cb_cls) +{ + struct DONAU_CharityGetHandle *cgh; + CURL *eh; + + TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", + url); + cgh = GNUNET_new (struct DONAU_CharityGetHandle); + cgh->url = GNUNET_strdup (url); + cgh->cb = cb; + cgh->charity_id = id; + cgh->cb_cls = cb_cls; + char arg_str[sizeof (id) * 2 + 32]; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "charities/%llu", + (unsigned long long) + id); + cgh->url = TALER_url_join (url, + arg_str, + NULL); + if (NULL == cgh->url) + { + GNUNET_free (cgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting a charity with URL `%s'.\n", + cgh->url); + eh = DONAU_curl_easy_get_ (cgh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (cgh->url); + GNUNET_free (cgh); + return NULL; + } + cgh->job = GNUNET_CURL_job_add_with_ct_json (ctx, + eh, + &handle_charity_get_finished, + cgh); + return cgh; +} + + +void +DONAU_charity_get_cancel ( + struct DONAU_CharityGetHandle *cgh) +{ + if (NULL != cgh->job) + { + GNUNET_CURL_job_cancel (cgh->job); + cgh->job = NULL; + } + GNUNET_free (cgh->url); + GNUNET_free (cgh); +} diff --git a/src/lib/donau_api_charity_post.c b/src/lib/donau_api_charity_post.c @@ -0,0 +1,264 @@ +/* + 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 + <http://www.gnu.org/licenses/> +*/ + +/** + * @file lib/donau_api_charity_post.c + * @brief Implementation of the "handle" component of the donau's HTTP API + * @author Lukas Matyja + */ +#include <gnunet/gnunet_curl_lib.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> +#include "donau_service.h" +#include "donau_api_curl_defaults.h" +#include "donau_json_lib.h" + + +/** + * Handle for a POST /charities/ request. + */ +struct DONAU_CharityPostHandle +{ + /** + * The url for the /charities/ 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_PostCharityResponseCallback cb; + + /** + * Closure to pass to @e cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + +/** + * Decode the JSON in @a resp_obj from the /charities/$ID response + * and store the data in the @a charity_data. + * + * @param[in] resp_obj JSON object to parse + * @param[out] charity_data where to store the results we decoded + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * (malformed JSON) + */ +//static enum GNUNET_GenericReturnValue +//handle_charity_get_ok (const json_t *resp_obj, +// struct DONAU_CharityGetHandle *cgh, +// struct DONAU_GetCharityResponse *gcresp) +//{ +// struct DONAU_Charity *charity = &gcresp->details.ok.charity; +// const char *name; +// if (JSON_OBJECT != json_typeof (resp_obj)) +// { +// GNUNET_break_op (0); +// return GNUNET_SYSERR; +// } +// +// struct GNUNET_JSON_Specification spec[] = { +// GNUNET_JSON_spec_fixed_auto ("charity_pub", +// &charity->charity_pub), +// GNUNET_JSON_spec_string ("name", &name), +// TALER_JSON_spec_amount_any ("max_per_year", +// &charity->max_per_year), +// TALER_JSON_spec_amount_any ("receipts_to_date", +// &charity-> +// receipts_to_date), +// GNUNET_JSON_spec_uint64 ("current_year", +// &charity->current_year), +// GNUNET_JSON_spec_end () +// }; +// if (GNUNET_OK != +// GNUNET_JSON_parse (resp_obj, +// spec, +// NULL, +// NULL)) +// { +// GNUNET_break_op (0); +// return GNUNET_SYSERR; +// } +// charity->name = GNUNET_strdup (name); +// +// cgh->cb (cgh->cb_cls, +// gcresp); +// cgh->cb = NULL; +// return GNUNET_OK; +//} + + +/** + * Function called when we're done processing the + * HTTP POST /charities 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_charity_post_finished (void *cls, + long response_code, + const void *resp_obj) +{ + struct DONAU_CharityPostHandle *cph = cls; + const json_t *j = resp_obj; + struct DONAU_PostCharityResponse pcresp = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + cph->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + pcresp.hr.ec = TALER_JSON_get_error_code (j); + pcresp.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + pcresp.hr.ec = TALER_JSON_get_error_code (j); + pcresp.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_CONTENT_TOO_LARGE: + pcresp.hr.ec = TALER_JSON_get_error_code (j); + pcresp.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + pcresp.hr.ec = TALER_JSON_get_error_code (j); + pcresp.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) pcresp.hr.ec, + cph->url); + break; + } + if (NULL != cph->cb) + { + cph->cb (cph->cb_cls, + &pcresp); + cph->cb = NULL; + } + DONAU_charity_post_cancel (cph); +} + + +struct DONAU_CharityPostHandle * +DONAU_charity_post ( + struct GNUNET_CURL_Context *ctx, + const char *url, + struct DONAU_CharityRequest *charity_req, + const struct DONAU_BearerToken bearer, + DONAU_PostCharityResponseCallback cb, + void *cb_cls) +{ + struct DONAU_CharityPostHandle *cph; + CURL *eh; + json_t *body; + + TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", + url); + cph = GNUNET_new (struct DONAU_CharityPostHandle); + cph->url = GNUNET_strdup (url); + cph->cb = cb; + cph->cb_cls = cb_cls; + cph->ctx = ctx; + cph->url = TALER_url_join (url, + "charities", + NULL); + if (NULL == cph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (cph); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "POST a charity with URL `%s'.\n", + cph->url); + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("charity_pub", + &charity_req->charity_pub), + GNUNET_JSON_pack_string ("url", + charity_req->charity_url), + GNUNET_JSON_pack_string ("name", + charity_req->name), + TALER_JSON_pack_amount ("max_per_year", + &charity_req->max_per_year), + TALER_JSON_pack_amount ("receipts_to_date", + &charity_req->receipts_to_date), // really needed? + GNUNET_JSON_pack_uint64 ("current_year", + charity_req->current_year)); + eh = DONAU_curl_easy_get_ (cph->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&cph->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (cph->url); + return NULL; + } + json_decref (body); + cph->job = GNUNET_CURL_job_add2 (ctx, + eh, + cph->post_ctx.headers, + &handle_charity_post_finished, + cph); + return cph; +} + + +void +DONAU_charity_post_cancel ( + struct DONAU_CharityPostHandle *cph) +{ + if (NULL != cph->job) + { + GNUNET_CURL_job_cancel (cph->job); + cph->job = NULL; + } + TALER_curl_easy_post_finished (&cph->post_ctx); + GNUNET_free (cph->url); + GNUNET_free (cph); +}