donau

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

commit a1f8d7afc2e686377fb8b4e195111d2fa129c265
parent 41f7458bfcbc73e859a060b525898f8381910cc3
Author: Matyja Lukas Adam <lukas.matyja@students.bfh.ch>
Date:   Mon, 15 Jan 2024 10:39:47 +0100

[lib] implement charity get API

Diffstat:
Msrc/lib/donau_api_charity_get.c | 300+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/lib/donau_api_handle.c | 2+-
2 files changed, 180 insertions(+), 122 deletions(-)

diff --git a/src/lib/donau_api_charity_get.c b/src/lib/donau_api_charity_get.c @@ -34,12 +34,6 @@ */ struct DONAU_CharityGetHandle { - - /** - * The donau base URL (i.e. "http://donau.taler.net/") - */ - char *donau_url; - /** * The url for the /charities/$CHARITY_ID request. */ @@ -51,19 +45,111 @@ struct DONAU_CharityGetHandle struct GNUNET_CURL_Job *job; /** - * Function to call with the donau's certification data, - * NULL if this has already been done. + * Function to call with the result. */ - DONAU_GetCharityResponseCallback cert_cb; + DONAU_GetCharityResponseCallback cb; /** - * Closure to pass to @e cert_cb. + * Charity id we are querying. */ - void *cert_cb_cls; + uint64_t 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) +{ + const json_t *charity_hist_array; + const char *name; + if (JSON_OBJECT != json_typeof (resp_obj)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + struct DONAU_GetCharityResponse charity_resp = { + .hr.reply = resp_obj, + .hr.http_status = MHD_HTTP_OK + }; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("name", + &name), + GNUNET_JSON_spec_fixed_auto ("pub_key", + &charity_resp.details.ok.charity->pub_key), + TALER_JSON_spec_amount_any ("max_per_year", + &charity_resp.details.ok.charity->max_per_year), + GNUNET_JSON_spec_array_const ("donation_history", + &charity_hist_array), + GNUNET_JSON_spec_uint32 ("num_hist", + &charity_resp.details.ok.charity->num_hist), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (resp_obj, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + charity_resp.details.ok.charity->name = GNUNET_strdup (name); + + /* parse the charity history data */ + charity_resp.details.ok.charity->num_hist + = json_array_size (charity_hist_array); + if (0 != charity_resp.details.ok.charity->num_hist) + { + json_t *charity_history_obj; + unsigned int index; + + charity_resp.details.ok.charity->donation_history + = GNUNET_new_array (charity_resp.details.ok.charity->num_hist, + struct DONAU_CharityHistoryYear); + json_array_foreach (charity_hist_array, index, charity_history_obj) { + struct DONAU_CharityHistoryYear *donation_history = &charity_resp.details.ok.charity->donation_history[index]; + struct GNUNET_JSON_Specification history_spec[] = { + TALER_JSON_spec_amount_any ("final_amount", + &donation_history->final_amount), + GNUNET_JSON_spec_uint32 ("year", + &donation_history->year), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (charity_history_obj, + history_spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + } + + cgh->cb (cgh->cb_cls, + &charity_resp); + cgh->cb = NULL; + return GNUNET_OK; +} + + +/** * Callback used when downloading the reply to a /charity request * is complete. * @@ -72,94 +158,70 @@ struct DONAU_CharityGetHandle * @param resp_obj parsed JSON result, NULL on error */ static void -charity_completed_cb (void *cls, +handle_charity_get_finished (void *cls, long response_code, const void *resp_obj) { - struct DONAU_CharityGetHandle *cgh = cls; - //const json_t *j = resp_obj; - //struct Charity *cd = NULL; + //struct DONAU_Charity *cd = NULL; - // struct DONAU_KeysResponse kresp = { - // .hr.reply = j, - // .hr.http_status = (unsigned int) response_code, - // .details.ok.compat = DONAU_VC_PROTOCOL_ERROR, - // }; - - // cgh->job = NULL; - // GNUNET_log (GNUNET_ERROR_TYPE_INFO, - // "Received keys from URL `%s' with status %ld.\n", - // cgh->url, - // response_code); - // switch (response_code) - // { - // case 0: - // GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - // "Failed to receive /keys response from donau %s\n", - // cgh->donau_url); - // break; - // case MHD_HTTP_OK: - // if (NULL == j) - // { - // GNUNET_break (0); - // response_code = 0; - // break; - // } - // kd = GNUNET_new (struct DONAU_Keys); - // kd->donau_url = GNUNET_strdup (cgh->donau_url); - - // if (GNUNET_OK != - // decode_keys_json (j, - // kd, - // &kresp.details.ok.compat)) - // { - // TALER_LOG_ERROR ("Could not decode /keys response\n"); - // kd->rc = 1; - // DONAU_keys_decref (kd); - // kd = NULL; - // kresp.hr.http_status = 0; - // kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - // break; - // } - // kd->rc = 1; - - // kresp.details.ok.keys = kd; - // break; - // case MHD_HTTP_BAD_REQUEST: - // case MHD_HTTP_UNAUTHORIZED: - // case MHD_HTTP_FORBIDDEN: - // case MHD_HTTP_NOT_FOUND: - // if (NULL == j) - // { - // kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - // kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec); - // } - // else - // { - // kresp.hr.ec = TALER_JSON_get_error_code (j); - // kresp.hr.hint = TALER_JSON_get_error_hint (j); - // } - // break; - // default: - // if (NULL == j) - // { - // kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - // kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec); - // } - // else - // { - // kresp.hr.ec = TALER_JSON_get_error_code (j); - // kresp.hr.hint = TALER_JSON_get_error_hint (j); - // } - // GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - // "Unexpected response code %u/%d\n", - // (unsigned int) response_code, - // (int) kresp.hr.ec); - // break; - // } - // cgh->cert_cb (cgh->cert_cb_cls, - // &kresp, - // kd); + 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.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); } @@ -167,8 +229,9 @@ struct DONAU_CharityGetHandle * DONAU_charity_get ( struct GNUNET_CURL_Context *ctx, const char *url, - const struct DONAU_BearerToken bearer, const uint64_t id, + const struct DONAU_BearerToken bearer, //TODO: check authorization + struct GNUNET_TIME_Relative timeout, DONAU_GetCharityResponseCallback cb, void *cb_cls) { @@ -178,18 +241,26 @@ DONAU_charity_get ( TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", url); cgh = GNUNET_new (struct DONAU_CharityGetHandle); - cgh->donau_url = GNUNET_strdup (url); - cgh->cert_cb = cb; - cgh->cert_cb_cls = cb_cls; - char arg_str[sizeof (struct DONAU_DonationUnitHashP) * 2 + 32]; - char id_str[sizeof (struct DONAU_DonationUnitHashP) * 2]; + cgh->url = GNUNET_strdup (url); + cgh->cb = cb; + cgh->charity_id = id; + cgh->cb_cls = cb_cls; + char arg_str[sizeof (id) * 2 + 32]; + char id_str[sizeof (id) * 2]; char *end; + char timeout_str[32]; end = GNUNET_STRINGS_data_to_string (&id, sizeof (id), id_str, sizeof (id_str)); *end = '\0'; + GNUNET_snprintf (timeout_str, + sizeof (timeout_str), + "%llu", + (unsigned long long) + (timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us)); GNUNET_snprintf (arg_str, sizeof (arg_str), "charities/%s", @@ -197,6 +268,11 @@ DONAU_charity_get ( 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); @@ -204,30 +280,13 @@ DONAU_charity_get ( if (NULL == eh) { GNUNET_break (0); - GNUNET_free (cgh->donau_url); GNUNET_free (cgh->url); GNUNET_free (cgh); return NULL; } - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_VERBOSE, - 0)); - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TIMEOUT, - 120 /* seconds */)); - // GNUNET_assert (CURLE_OK == - // curl_easy_setopt (eh, - // CURLOPT_HEADERFUNCTION, - // &header_cb)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_HEADERDATA, - cgh)); - cgh->job = GNUNET_CURL_job_add_with_ct_json (ctx, + cgh->job = GNUNET_CURL_job_add (ctx, eh, - &charity_completed_cb, + &handle_charity_get_finished, cgh); return cgh; } @@ -241,7 +300,6 @@ DONAU_charity_get_cancel ( GNUNET_CURL_job_cancel (cgh->job); cgh->job = NULL; } - GNUNET_free (cgh->donau_url); GNUNET_free (cgh->url); GNUNET_free (cgh); } \ No newline at end of file diff --git a/src/lib/donau_api_handle.c b/src/lib/donau_api_handle.c @@ -452,7 +452,7 @@ keys_completed_cb (void *cls, struct DONAU_KeysResponse kresp = { .hr.reply = j, .hr.http_status = (unsigned int) response_code, - .details.ok.compat = DONAU_VC_PROTOCOL_ERROR, + .details.ok.compat = DONAU_VC_PROTOCOL_ERROR }; gkh->job = NULL;