/* This file is part of TALER Copyright (C) 2014-2017, 2020 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TALER; see the file COPYING.LGPL. If not, see */ /** * @file lib/merchant_api_get_reserves.c * @brief Implementation of the GET /reserves request of the merchant's HTTP API * @author Marcello Stanisci * @author Christian Grothoff */ #include "platform.h" #include #include #include /* just for HTTP status codes */ #include #include #include "taler_merchant_service.h" #include #include /** * @brief A Handle for tracking wire reserves. */ struct TALER_MERCHANT_ReservesGetHandle { /** * The url for this request. */ char *url; /** * Handle for the request. */ struct GNUNET_CURL_Job *job; /** * Function to call with the result. */ TALER_MERCHANT_ReservesGetCallback cb; /** * Closure for @a cb. */ void *cb_cls; /** * Reference to the execution context. */ struct GNUNET_CURL_Context *ctx; }; /** * Function called when we're done processing the * HTTP GET /reserves request. * * @param cls the `struct TALER_MERCHANT_ReservesGetHandle` * @param response_code HTTP response code, 0 on error * @param json response body, NULL if not in JSON */ static void handle_reserves_get_finished (void *cls, long response_code, const void *response) { struct TALER_MERCHANT_ReservesGetHandle *rgh = cls; const json_t *json = response; struct TALER_MERCHANT_HttpResponse hr = { .http_status = (unsigned int) response_code, .reply = json }; rgh->job = NULL; switch (response_code) { case 0: hr.ec = TALER_EC_INVALID_RESPONSE; break; case MHD_HTTP_OK: { json_t *reserves; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_json ("reserves", &reserves), GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (json, spec, NULL, NULL)) { GNUNET_break_op (0); hr.http_status = 0; hr.ec = TALER_EC_INVALID_RESPONSE; break; } else { size_t rds_length; struct TALER_MERCHANT_ReserveSummary *rds; json_t *reserve; unsigned int i; bool ok; if (! json_is_array (reserves)) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); hr.http_status = 0; hr.ec = TALER_EC_INVALID_RESPONSE; break; } rds_length = json_array_size (reserves); rds = GNUNET_new_array (rds_length, struct TALER_MERCHANT_ReserveSummary); ok = true; json_array_foreach (reserves, i, reserve) { struct TALER_MERCHANT_ReserveSummary *rd = &rds[i]; struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_fixed_auto ("reserve_pub", &rd->reserve_pub), TALER_JSON_spec_absolute_time ("creation_time", &rd->creation_time), TALER_JSON_spec_absolute_time ("expiration_time", &rd->expiration_time), TALER_JSON_spec_amount ("merchant_initial_amount", &rd->merchant_initial_amount), TALER_JSON_spec_amount ("exchange_initial_amount", &rd->exchange_initial_amount), TALER_JSON_spec_amount ("pickup_amount", &rd->pickup_amount), TALER_JSON_spec_amount ("committed_amount", &rd->committed_amount), GNUNET_JSON_spec_bool ("active", &rd->active), GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (reserve, ispec, NULL, NULL)) { GNUNET_break_op (0); ok = false; break; } } if (! ok) { GNUNET_break_op (0); GNUNET_free (rds); GNUNET_JSON_parse_free (spec); hr.http_status = 0; hr.ec = TALER_EC_INVALID_RESPONSE; break; } rgh->cb (rgh->cb_cls, &hr, rds_length, rds); GNUNET_free (rds); GNUNET_JSON_parse_free (spec); TALER_MERCHANT_reserves_get_cancel (rgh); return; } } case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ hr.ec = TALER_JSON_get_error_code (json); hr.hint = TALER_JSON_get_error_hint (json); break; default: /* unexpected response code */ GNUNET_break_op (0); TALER_MERCHANT_parse_error_details_ (json, response_code, &hr); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, (int) hr.ec); response_code = 0; break; } rgh->cb (rgh->cb_cls, &hr, 0, NULL); TALER_MERCHANT_reserves_get_cancel (rgh); } /** * Issue a GET /reserves request to the backend. Informs the backend * that a customer wants to pick up a reserves. * * @param ctx execution context * @param backend_url base URL of the merchant backend * @param after filter for reserves created after this date, use 0 for no filtering * @param active filter for reserves that are active * @param failures filter for reserves where we disagree about the balance with * the exchange * @param cb function to call with the result(s) * @param cb_cls closure for @a cb * @return handle for this operation, NULL upon errors */ struct TALER_MERCHANT_ReservesGetHandle * TALER_MERCHANT_reserves_get (struct GNUNET_CURL_Context *ctx, const char *backend_url, struct GNUNET_TIME_Absolute after, enum TALER_EXCHANGE_YesNoAll active, enum TALER_EXCHANGE_YesNoAll failures, TALER_MERCHANT_ReservesGetCallback cb, void *cb_cls) { struct TALER_MERCHANT_ReservesGetHandle *rgh; CURL *eh; const char *active_s = NULL; const char *failures_s = NULL; char *after_s; rgh = GNUNET_new (struct TALER_MERCHANT_ReservesGetHandle); rgh->ctx = ctx; rgh->cb = cb; rgh->cb_cls = cb_cls; active_s = TALER_yna_to_string (active); failures_s = TALER_yna_to_string (failures); after_s = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string ( after)); rgh->url = TALER_url_join (backend_url, "private/reserves", "active", active_s, "failures", failures_s, "after", after.abs_value_us != 0 ? after_s : NULL, NULL); GNUNET_free (after_s); if (NULL == rgh->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not construct request URL.\n"); GNUNET_free (rgh); return NULL; } eh = curl_easy_init (); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_URL, rgh->url)); rgh->job = GNUNET_CURL_job_add (ctx, eh, &handle_reserves_get_finished, rgh); return rgh; } /** * Cancel a GET /reserves request. * * @param rgh handle to the request to be canceled */ void TALER_MERCHANT_reserves_get_cancel ( struct TALER_MERCHANT_ReservesGetHandle *rgh) { if (NULL != rgh->job) { GNUNET_CURL_job_cancel (rgh->job); rgh->job = NULL; } GNUNET_free (rgh->url); GNUNET_free (rgh); } /* end of merchant_api_get_reserves.c */