Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2017, 2020 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser General Public License as published by the Free Software
7 : Foundation; either version 2.1, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
12 :
13 : You should have received a copy of the GNU Lesser General Public License along with
14 : TALER; see the file COPYING.LGPL. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file merchant_api_get_reserves.c
19 : * @brief Implementation of the GET /reserves request of the merchant's HTTP API
20 : * @author Marcello Stanisci
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h"
24 : #include <curl/curl.h>
25 : #include <jansson.h>
26 : #include <microhttpd.h> /* just for HTTP status codes */
27 : #include <gnunet/gnunet_util_lib.h>
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include "taler_merchant_service.h"
30 : #include "merchant_api_curl_defaults.h"
31 : #include <taler/taler_json_lib.h>
32 : #include <taler/taler_signatures.h>
33 :
34 :
35 : /**
36 : * @brief A Handle for tracking wire reserves.
37 : */
38 : struct TALER_MERCHANT_ReservesGetHandle
39 : {
40 :
41 : /**
42 : * The url for this request.
43 : */
44 : char *url;
45 :
46 : /**
47 : * Handle for the request.
48 : */
49 : struct GNUNET_CURL_Job *job;
50 :
51 : /**
52 : * Function to call with the result.
53 : */
54 : TALER_MERCHANT_ReservesGetCallback cb;
55 :
56 : /**
57 : * Closure for @a cb.
58 : */
59 : void *cb_cls;
60 :
61 : /**
62 : * Reference to the execution context.
63 : */
64 : struct GNUNET_CURL_Context *ctx;
65 : };
66 :
67 :
68 : /**
69 : * Function called when we're done processing the
70 : * HTTP GET /reserves request.
71 : *
72 : * @param cls the `struct TALER_MERCHANT_ReservesGetHandle`
73 : * @param response_code HTTP response code, 0 on error
74 : * @param response response body, NULL if not in JSON
75 : */
76 : static void
77 0 : handle_reserves_get_finished (void *cls,
78 : long response_code,
79 : const void *response)
80 : {
81 0 : struct TALER_MERCHANT_ReservesGetHandle *rgh = cls;
82 0 : const json_t *json = response;
83 0 : struct TALER_MERCHANT_HttpResponse hr = {
84 0 : .http_status = (unsigned int) response_code,
85 : .reply = json
86 : };
87 :
88 0 : rgh->job = NULL;
89 0 : switch (response_code)
90 : {
91 0 : case 0:
92 0 : hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
93 0 : break;
94 0 : case MHD_HTTP_OK:
95 : {
96 : json_t *reserves;
97 : struct GNUNET_JSON_Specification spec[] = {
98 0 : GNUNET_JSON_spec_json ("reserves",
99 : &reserves),
100 0 : GNUNET_JSON_spec_end ()
101 : };
102 :
103 0 : if (GNUNET_OK !=
104 0 : GNUNET_JSON_parse (json,
105 : spec,
106 : NULL, NULL))
107 : {
108 0 : GNUNET_break_op (0);
109 0 : hr.http_status = 0;
110 0 : hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
111 0 : break;
112 : }
113 : else
114 : {
115 : size_t rds_length;
116 : struct TALER_MERCHANT_ReserveSummary *rds;
117 : json_t *reserve;
118 : unsigned int i;
119 : bool ok;
120 :
121 0 : if (! json_is_array (reserves))
122 : {
123 0 : GNUNET_break_op (0);
124 0 : GNUNET_JSON_parse_free (spec);
125 0 : hr.http_status = 0;
126 0 : hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
127 0 : break;
128 : }
129 0 : rds_length = json_array_size (reserves);
130 0 : rds = GNUNET_new_array (rds_length,
131 : struct TALER_MERCHANT_ReserveSummary);
132 0 : ok = true;
133 0 : json_array_foreach (reserves, i, reserve) {
134 0 : struct TALER_MERCHANT_ReserveSummary *rd = &rds[i];
135 : struct GNUNET_JSON_Specification ispec[] = {
136 0 : GNUNET_JSON_spec_fixed_auto ("reserve_pub",
137 : &rd->reserve_pub),
138 0 : GNUNET_JSON_spec_timestamp ("creation_time",
139 : &rd->creation_time),
140 0 : GNUNET_JSON_spec_timestamp ("expiration_time",
141 : &rd->expiration_time),
142 0 : TALER_JSON_spec_amount_any ("merchant_initial_amount",
143 : &rd->merchant_initial_amount),
144 0 : TALER_JSON_spec_amount_any ("exchange_initial_amount",
145 : &rd->exchange_initial_amount),
146 0 : TALER_JSON_spec_amount_any ("pickup_amount",
147 : &rd->pickup_amount),
148 0 : TALER_JSON_spec_amount_any ("committed_amount",
149 : &rd->committed_amount),
150 0 : GNUNET_JSON_spec_bool ("active",
151 : &rd->active),
152 0 : GNUNET_JSON_spec_end ()
153 : };
154 :
155 0 : if (GNUNET_OK !=
156 0 : GNUNET_JSON_parse (reserve,
157 : ispec,
158 : NULL, NULL))
159 : {
160 0 : GNUNET_break_op (0);
161 0 : ok = false;
162 0 : break;
163 : }
164 : }
165 :
166 0 : if (! ok)
167 : {
168 0 : GNUNET_break_op (0);
169 0 : GNUNET_free (rds);
170 0 : GNUNET_JSON_parse_free (spec);
171 0 : hr.http_status = 0;
172 0 : hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
173 0 : break;
174 : }
175 0 : rgh->cb (rgh->cb_cls,
176 : &hr,
177 : rds_length,
178 : rds);
179 0 : GNUNET_free (rds);
180 0 : GNUNET_JSON_parse_free (spec);
181 0 : TALER_MERCHANT_reserves_get_cancel (rgh);
182 0 : return;
183 : }
184 : }
185 0 : case MHD_HTTP_UNAUTHORIZED:
186 0 : hr.ec = TALER_JSON_get_error_code (json);
187 0 : hr.hint = TALER_JSON_get_error_hint (json);
188 : /* Nothing really to verify, merchant says we need to authenticate. */
189 0 : break;
190 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
191 : /* Server had an internal issue; we should retry, but this API
192 : leaves this to the application */
193 0 : hr.ec = TALER_JSON_get_error_code (json);
194 0 : hr.hint = TALER_JSON_get_error_hint (json);
195 0 : break;
196 0 : default:
197 : /* unexpected response code */
198 0 : GNUNET_break_op (0);
199 0 : TALER_MERCHANT_parse_error_details_ (json,
200 : response_code,
201 : &hr);
202 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
203 : "Unexpected response code %u/%d\n",
204 : (unsigned int) response_code,
205 : (int) hr.ec);
206 0 : response_code = 0;
207 0 : break;
208 : }
209 0 : rgh->cb (rgh->cb_cls,
210 : &hr,
211 : 0,
212 : NULL);
213 0 : TALER_MERCHANT_reserves_get_cancel (rgh);
214 : }
215 :
216 :
217 : struct TALER_MERCHANT_ReservesGetHandle *
218 0 : TALER_MERCHANT_reserves_get (struct GNUNET_CURL_Context *ctx,
219 : const char *backend_url,
220 : struct GNUNET_TIME_Timestamp after,
221 : enum TALER_EXCHANGE_YesNoAll active,
222 : enum TALER_EXCHANGE_YesNoAll failures,
223 : TALER_MERCHANT_ReservesGetCallback cb,
224 : void *cb_cls)
225 : {
226 : struct TALER_MERCHANT_ReservesGetHandle *rgh;
227 : CURL *eh;
228 0 : const char *active_s = NULL;
229 0 : const char *failures_s = NULL;
230 : char *after_s;
231 :
232 0 : rgh = GNUNET_new (struct TALER_MERCHANT_ReservesGetHandle);
233 0 : rgh->ctx = ctx;
234 0 : rgh->cb = cb;
235 0 : rgh->cb_cls = cb_cls;
236 0 : active_s = TALER_yna_to_string (active);
237 0 : failures_s = TALER_yna_to_string (failures);
238 0 : after_s = GNUNET_strdup (GNUNET_TIME_timestamp2s (after));
239 0 : rgh->url = TALER_url_join (backend_url,
240 : "private/reserves",
241 : "active",
242 : active_s,
243 : "failures",
244 : failures_s,
245 : "after",
246 0 : GNUNET_TIME_absolute_is_zero (after.abs_time)
247 : ? NULL
248 : : after_s,
249 : NULL);
250 0 : GNUNET_free (after_s);
251 0 : if (NULL == rgh->url)
252 : {
253 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
254 : "Could not construct request URL.\n");
255 0 : GNUNET_free (rgh);
256 0 : return NULL;
257 : }
258 0 : eh = TALER_MERCHANT_curl_easy_get_ (rgh->url);
259 0 : rgh->job = GNUNET_CURL_job_add (ctx,
260 : eh,
261 : &handle_reserves_get_finished,
262 : rgh);
263 0 : return rgh;
264 : }
265 :
266 :
267 : void
268 0 : TALER_MERCHANT_reserves_get_cancel (
269 : struct TALER_MERCHANT_ReservesGetHandle *rgh)
270 : {
271 0 : if (NULL != rgh->job)
272 : {
273 0 : GNUNET_CURL_job_cancel (rgh->job);
274 0 : rgh->job = NULL;
275 : }
276 0 : GNUNET_free (rgh->url);
277 0 : GNUNET_free (rgh);
278 0 : }
279 :
280 :
281 : /* end of merchant_api_get_reserves.c */
|