Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2023 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_transfers.c
19 : * @brief Implementation of the GET /transfers 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_common.h"
31 : #include "merchant_api_curl_defaults.h"
32 : #include <taler/taler_json_lib.h>
33 : #include <taler/taler_signatures.h>
34 :
35 :
36 : /**
37 : * @brief A Handle for tracking wire transfers.
38 : */
39 : struct TALER_MERCHANT_GetTransfersHandle
40 : {
41 :
42 : /**
43 : * The url for this request.
44 : */
45 : char *url;
46 :
47 : /**
48 : * Handle for the request.
49 : */
50 : struct GNUNET_CURL_Job *job;
51 :
52 : /**
53 : * Function to call with the result.
54 : */
55 : TALER_MERCHANT_GetTransfersCallback cb;
56 :
57 : /**
58 : * Closure for @a cb.
59 : */
60 : void *cb_cls;
61 :
62 : /**
63 : * Reference to the execution context.
64 : */
65 : struct GNUNET_CURL_Context *ctx;
66 : };
67 :
68 :
69 : /**
70 : * Function called when we're done processing the
71 : * HTTP GET /transfers request.
72 : *
73 : * @param cls the `struct TALER_MERCHANT_GetTransfersHandle`
74 : * @param response_code HTTP response code, 0 on error
75 : * @param response response body, NULL if not in JSON
76 : */
77 : static void
78 4 : handle_transfers_get_finished (void *cls,
79 : long response_code,
80 : const void *response)
81 : {
82 4 : struct TALER_MERCHANT_GetTransfersHandle *gth = cls;
83 4 : const json_t *json = response;
84 4 : struct TALER_MERCHANT_GetTransfersResponse gtr = {
85 4 : .hr.http_status = (unsigned int) response_code,
86 : .hr.reply = json
87 : };
88 :
89 4 : gth->job = NULL;
90 4 : switch (response_code)
91 : {
92 0 : case 0:
93 0 : gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
94 0 : break;
95 4 : case MHD_HTTP_OK:
96 : {
97 : const json_t *transfers;
98 : struct GNUNET_JSON_Specification spec[] = {
99 4 : GNUNET_JSON_spec_array_const ("transfers",
100 : &transfers),
101 4 : GNUNET_JSON_spec_end ()
102 : };
103 :
104 4 : if (GNUNET_OK !=
105 4 : GNUNET_JSON_parse (json,
106 : spec,
107 : NULL, NULL))
108 : {
109 0 : GNUNET_break_op (0);
110 0 : gtr.hr.http_status = 0;
111 0 : gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
112 0 : break;
113 : }
114 :
115 : {
116 : size_t tds_length;
117 : struct TALER_MERCHANT_TransferData *tds;
118 : json_t *transfer;
119 : size_t i;
120 : bool ok;
121 :
122 4 : tds_length = json_array_size (transfers);
123 4 : tds = GNUNET_new_array (tds_length,
124 : struct TALER_MERCHANT_TransferData);
125 4 : ok = true;
126 11 : json_array_foreach (transfers, i, transfer) {
127 7 : struct TALER_MERCHANT_TransferData *td = &tds[i];
128 : struct GNUNET_JSON_Specification ispec[] = {
129 7 : TALER_JSON_spec_amount_any ("credit_amount",
130 : &td->credit_amount),
131 7 : GNUNET_JSON_spec_fixed_auto ("wtid",
132 : &td->wtid),
133 7 : TALER_JSON_spec_full_payto_uri ("payto_uri",
134 : &td->payto_uri),
135 7 : TALER_JSON_spec_web_url ("exchange_url",
136 : &td->exchange_url),
137 7 : GNUNET_JSON_spec_uint64 ("transfer_serial_id",
138 : &td->credit_serial),
139 7 : GNUNET_JSON_spec_mark_optional (
140 : GNUNET_JSON_spec_timestamp ("execution_time",
141 : &td->execution_time),
142 : NULL),
143 7 : GNUNET_JSON_spec_bool ("expected",
144 : &td->expected),
145 7 : GNUNET_JSON_spec_end ()
146 : };
147 :
148 7 : if (GNUNET_OK !=
149 7 : GNUNET_JSON_parse (transfer,
150 : ispec,
151 : NULL, NULL))
152 : {
153 0 : GNUNET_break_op (0);
154 0 : ok = false;
155 0 : break;
156 : }
157 : }
158 :
159 4 : if (! ok)
160 : {
161 0 : GNUNET_break_op (0);
162 0 : GNUNET_free (tds);
163 0 : gtr.hr.http_status = 0;
164 0 : gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
165 0 : break;
166 : }
167 4 : gtr.details.ok.transfers = tds;
168 4 : gtr.details.ok.transfers_length = tds_length;
169 4 : gth->cb (gth->cb_cls,
170 : >r);
171 4 : GNUNET_free (tds);
172 4 : TALER_MERCHANT_transfers_get_cancel (gth);
173 4 : return;
174 : }
175 : }
176 0 : case MHD_HTTP_UNAUTHORIZED:
177 0 : gtr.hr.ec = TALER_JSON_get_error_code (json);
178 0 : gtr.hr.hint = TALER_JSON_get_error_hint (json);
179 : /* Nothing really to verify, merchant says we need to authenticate. */
180 0 : break;
181 0 : case MHD_HTTP_NOT_FOUND:
182 : /* Nothing really to verify, this should never
183 : happen, we should pass the JSON reply to the application */
184 0 : gtr.hr.ec = TALER_JSON_get_error_code (json);
185 0 : gtr.hr.hint = TALER_JSON_get_error_hint (json);
186 0 : break;
187 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
188 : /* Server had an internal issue; we should retry, but this API
189 : leaves this to the application */
190 0 : gtr.hr.ec = TALER_JSON_get_error_code (json);
191 0 : gtr.hr.hint = TALER_JSON_get_error_hint (json);
192 0 : break;
193 0 : default:
194 : /* unexpected response code */
195 0 : GNUNET_break_op (0);
196 0 : TALER_MERCHANT_parse_error_details_ (json,
197 : response_code,
198 : >r.hr);
199 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
200 : "Unexpected response code %u/%d\n",
201 : (unsigned int) response_code,
202 : (int) gtr.hr.ec);
203 0 : gtr.hr.http_status = 0;
204 0 : break;
205 : }
206 0 : gth->cb (gth->cb_cls,
207 : >r);
208 0 : TALER_MERCHANT_transfers_get_cancel (gth);
209 : }
210 :
211 :
212 : struct TALER_MERCHANT_GetTransfersHandle *
213 4 : TALER_MERCHANT_transfers_get (
214 : struct GNUNET_CURL_Context *ctx,
215 : const char *backend_url,
216 : struct TALER_FullPayto payto_uri,
217 : const struct GNUNET_TIME_Timestamp before,
218 : const struct GNUNET_TIME_Timestamp after,
219 : int64_t limit,
220 : uint64_t offset,
221 : enum TALER_EXCHANGE_YesNoAll expected,
222 : TALER_MERCHANT_GetTransfersCallback cb,
223 : void *cb_cls)
224 : {
225 : struct TALER_MERCHANT_GetTransfersHandle *gth;
226 : CURL *eh;
227 4 : const char *expected_s = NULL;
228 : char limit_s[30];
229 : char offset_s[30];
230 : char before_s[30];
231 : char after_s[30];
232 :
233 4 : gth = GNUNET_new (struct TALER_MERCHANT_GetTransfersHandle);
234 4 : gth->ctx = ctx;
235 4 : gth->cb = cb;
236 4 : gth->cb_cls = cb_cls;
237 4 : expected_s = TALER_yna_to_string (expected);
238 4 : GNUNET_snprintf (limit_s,
239 : sizeof (limit_s),
240 : "%lld",
241 : (long long) limit);
242 4 : GNUNET_snprintf (offset_s,
243 : sizeof (offset_s),
244 : "%lld",
245 : (unsigned long long) offset);
246 4 : GNUNET_snprintf (before_s,
247 : sizeof (before_s),
248 : "%llu",
249 4 : (unsigned long long) GNUNET_TIME_timestamp_to_s (before));
250 4 : GNUNET_snprintf (after_s,
251 : sizeof (after_s),
252 : "%llu",
253 4 : (unsigned long long) GNUNET_TIME_timestamp_to_s (after));
254 : {
255 4 : char *enc_payto = TALER_urlencode (payto_uri.full_payto);
256 :
257 8 : gth->url = TALER_url_join (backend_url,
258 : "private/transfers",
259 : "payto_uri",
260 : enc_payto,
261 : "expected",
262 : (TALER_EXCHANGE_YNA_ALL != expected)
263 : ? expected_s
264 : : NULL,
265 : "limit",
266 : 0 != limit
267 : ? limit_s
268 : : NULL,
269 : "offset",
270 0 : ((0 != offset) && (UINT64_MAX != offset))
271 : ? offset_s
272 : : NULL,
273 : "before",
274 4 : GNUNET_TIME_absolute_is_never (before.abs_time)
275 : ? NULL
276 : : before_s,
277 : "after",
278 4 : GNUNET_TIME_absolute_is_zero (after.abs_time)
279 : ? NULL
280 : : after_s,
281 : NULL);
282 4 : GNUNET_free (enc_payto);
283 : }
284 4 : if (NULL == gth->url)
285 : {
286 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
287 : "Could not construct request URL.\n");
288 0 : GNUNET_free (gth);
289 0 : return NULL;
290 : }
291 4 : eh = TALER_MERCHANT_curl_easy_get_ (gth->url);
292 4 : gth->job = GNUNET_CURL_job_add (ctx,
293 : eh,
294 : &handle_transfers_get_finished,
295 : gth);
296 4 : return gth;
297 : }
298 :
299 :
300 : void
301 4 : TALER_MERCHANT_transfers_get_cancel (
302 : struct TALER_MERCHANT_GetTransfersHandle *gth)
303 : {
304 4 : if (NULL != gth->job)
305 : {
306 0 : GNUNET_CURL_job_cancel (gth->job);
307 0 : gth->job = NULL;
308 : }
309 4 : GNUNET_free (gth->url);
310 4 : GNUNET_free (gth);
311 4 : }
312 :
313 :
314 : /* end of merchant_api_get_transfers.c */
|