Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2021 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_reserve.c
19 : * @brief Implementation of the GET /reserve 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 reserve.
37 : */
38 : struct TALER_MERCHANT_ReserveGetHandle
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_ReserveGetCallback 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 /reserve request.
71 : *
72 : * @param cls the `struct TALER_MERCHANT_ReserveGetHandle`
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_reserve_get_finished (void *cls,
78 : long response_code,
79 : const void *response)
80 : {
81 0 : struct TALER_MERCHANT_ReserveGetHandle *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 : bool active;
88 :
89 0 : rgh->job = NULL;
90 0 : switch (response_code)
91 : {
92 0 : case 0:
93 0 : hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
94 0 : break;
95 0 : case MHD_HTTP_OK:
96 : {
97 : struct TALER_MERCHANT_ReserveSummary rs;
98 : const json_t *tips;
99 0 : const char *exchange_url = NULL;
100 0 : const char *payto_uri = NULL;
101 : struct GNUNET_JSON_Specification spec[] = {
102 0 : GNUNET_JSON_spec_timestamp ("creation_time",
103 : &rs.creation_time),
104 0 : GNUNET_JSON_spec_timestamp ("expiration_time",
105 : &rs.expiration_time),
106 0 : GNUNET_JSON_spec_bool ("active",
107 : &active),
108 0 : GNUNET_JSON_spec_mark_optional (
109 : GNUNET_JSON_spec_string ("exchange_url",
110 : &exchange_url),
111 : NULL),
112 0 : GNUNET_JSON_spec_mark_optional (
113 : GNUNET_JSON_spec_string ("payto_uri",
114 : &payto_uri),
115 : NULL),
116 0 : TALER_JSON_spec_amount_any ("merchant_initial_amount",
117 : &rs.merchant_initial_amount),
118 0 : TALER_JSON_spec_amount_any ("exchange_initial_amount",
119 : &rs.exchange_initial_amount),
120 0 : TALER_JSON_spec_amount_any ("pickup_amount",
121 : &rs.pickup_amount),
122 0 : TALER_JSON_spec_amount_any ("committed_amount",
123 : &rs.committed_amount),
124 0 : GNUNET_JSON_spec_end ()
125 : };
126 :
127 0 : if (GNUNET_OK !=
128 0 : GNUNET_JSON_parse (json,
129 : spec,
130 : NULL, NULL))
131 : {
132 0 : GNUNET_break_op (0);
133 0 : hr.http_status = 0;
134 0 : hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
135 0 : break;
136 : }
137 :
138 0 : tips = json_object_get (json,
139 : "tips");
140 0 : if ((NULL == tips) ||
141 0 : json_is_null (tips))
142 : {
143 0 : rgh->cb (rgh->cb_cls,
144 : &hr,
145 : &rs,
146 : false,
147 : NULL,
148 : NULL,
149 : 0,
150 : NULL);
151 0 : TALER_MERCHANT_reserve_get_cancel (rgh);
152 0 : return;
153 : }
154 0 : if (! json_is_array (tips))
155 : {
156 0 : GNUNET_break_op (0);
157 0 : hr.http_status = 0;
158 0 : hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
159 0 : break;
160 : }
161 : {
162 : size_t tds_length;
163 : json_t *tip;
164 : struct TALER_MERCHANT_TipDetails *tds;
165 : unsigned int i;
166 : bool ok;
167 :
168 0 : tds_length = json_array_size (tips);
169 0 : tds = GNUNET_new_array (tds_length,
170 : struct TALER_MERCHANT_TipDetails);
171 0 : ok = true;
172 0 : json_array_foreach (tips, i, tip) {
173 0 : struct TALER_MERCHANT_TipDetails *td = &tds[i];
174 : struct GNUNET_JSON_Specification ispec[] = {
175 0 : GNUNET_JSON_spec_fixed_auto ("tip_id",
176 : &td->tip_id),
177 0 : TALER_JSON_spec_amount_any ("total_amount",
178 : &td->amount),
179 0 : GNUNET_JSON_spec_string ("reason",
180 : &td->reason),
181 0 : GNUNET_JSON_spec_end ()
182 : };
183 :
184 0 : if (GNUNET_OK !=
185 0 : GNUNET_JSON_parse (tip,
186 : ispec,
187 : NULL, NULL))
188 : {
189 0 : GNUNET_break_op (0);
190 0 : ok = false;
191 0 : break;
192 : }
193 : }
194 :
195 0 : if (! ok)
196 : {
197 0 : GNUNET_break_op (0);
198 0 : GNUNET_free (tds);
199 0 : hr.http_status = 0;
200 0 : hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
201 0 : break;
202 : }
203 0 : rgh->cb (rgh->cb_cls,
204 : &hr,
205 : &rs,
206 : active,
207 : exchange_url,
208 : payto_uri,
209 : tds_length,
210 : tds);
211 0 : GNUNET_free (tds);
212 0 : TALER_MERCHANT_reserve_get_cancel (rgh);
213 0 : return;
214 : }
215 : }
216 0 : case MHD_HTTP_UNAUTHORIZED:
217 0 : hr.ec = TALER_JSON_get_error_code (json);
218 0 : hr.hint = TALER_JSON_get_error_hint (json);
219 : /* Nothing really to verify, merchant says we need to authenticate. */
220 0 : break;
221 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
222 : /* Server had an internal issue; we should retry, but this API
223 : leaves this to the application */
224 0 : hr.ec = TALER_JSON_get_error_code (json);
225 0 : hr.hint = TALER_JSON_get_error_hint (json);
226 0 : break;
227 0 : default:
228 : /* unexpected response code */
229 0 : GNUNET_break_op (0);
230 0 : TALER_MERCHANT_parse_error_details_ (json,
231 : response_code,
232 : &hr);
233 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
234 : "Unexpected response code %u/%d\n",
235 : (unsigned int) response_code,
236 : (int) hr.ec);
237 0 : response_code = 0;
238 0 : break;
239 : }
240 0 : rgh->cb (rgh->cb_cls,
241 : &hr,
242 : NULL,
243 : false,
244 : NULL,
245 : NULL,
246 : 0,
247 : NULL);
248 0 : TALER_MERCHANT_reserve_get_cancel (rgh);
249 : }
250 :
251 :
252 : struct TALER_MERCHANT_ReserveGetHandle *
253 0 : TALER_MERCHANT_reserve_get (struct GNUNET_CURL_Context *ctx,
254 : const char *backend_url,
255 : const struct TALER_ReservePublicKeyP *reserve_pub,
256 : bool fetch_tips,
257 : TALER_MERCHANT_ReserveGetCallback cb,
258 : void *cb_cls)
259 : {
260 : struct TALER_MERCHANT_ReserveGetHandle *rgh;
261 : CURL *eh;
262 :
263 0 : rgh = GNUNET_new (struct TALER_MERCHANT_ReserveGetHandle);
264 0 : rgh->ctx = ctx;
265 0 : rgh->cb = cb;
266 0 : rgh->cb_cls = cb_cls;
267 : {
268 : char res_str[sizeof (*reserve_pub) * 2];
269 : char arg_str[sizeof (res_str) + 32];
270 : char *end;
271 :
272 0 : end = GNUNET_STRINGS_data_to_string (reserve_pub,
273 : sizeof (*reserve_pub),
274 : res_str,
275 : sizeof (res_str));
276 0 : *end = '\0';
277 0 : GNUNET_snprintf (arg_str,
278 : sizeof (arg_str),
279 : "private/reserves/%s",
280 : res_str);
281 0 : rgh->url = TALER_url_join (backend_url,
282 : arg_str,
283 : "tips",
284 : fetch_tips ? "yes" : "no",
285 : NULL);
286 : }
287 0 : if (NULL == rgh->url)
288 : {
289 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
290 : "Could not construct request URL.\n");
291 0 : GNUNET_free (rgh);
292 0 : return NULL;
293 : }
294 0 : eh = TALER_MERCHANT_curl_easy_get_ (rgh->url);
295 0 : rgh->job = GNUNET_CURL_job_add (ctx,
296 : eh,
297 : &handle_reserve_get_finished,
298 : rgh);
299 0 : return rgh;
300 : }
301 :
302 :
303 : void
304 0 : TALER_MERCHANT_reserve_get_cancel (
305 : struct TALER_MERCHANT_ReserveGetHandle *rgh)
306 : {
307 0 : if (NULL != rgh->job)
308 : {
309 0 : GNUNET_CURL_job_cancel (rgh->job);
310 0 : rgh->job = NULL;
311 : }
312 0 : GNUNET_free (rgh->url);
313 0 : GNUNET_free (rgh);
314 0 : }
315 :
316 :
317 : /* end of merchant_api_get_reserve.c */
|