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_timestamp ("execution_time",
140 : &td->execution_time),
141 7 : GNUNET_JSON_spec_bool ("expected",
142 : &td->expected),
143 7 : GNUNET_JSON_spec_end ()
144 : };
145 :
146 7 : if (GNUNET_OK !=
147 7 : GNUNET_JSON_parse (transfer,
148 : ispec,
149 : NULL, NULL))
150 : {
151 0 : GNUNET_break_op (0);
152 0 : ok = false;
153 0 : break;
154 : }
155 : }
156 :
157 4 : if (! ok)
158 : {
159 0 : GNUNET_break_op (0);
160 0 : GNUNET_free (tds);
161 0 : gtr.hr.http_status = 0;
162 0 : gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
163 0 : break;
164 : }
165 4 : gtr.details.ok.transfers = tds;
166 4 : gtr.details.ok.transfers_length = tds_length;
167 4 : gth->cb (gth->cb_cls,
168 : >r);
169 4 : GNUNET_free (tds);
170 4 : TALER_MERCHANT_transfers_get_cancel (gth);
171 4 : return;
172 : }
173 : }
174 0 : case MHD_HTTP_UNAUTHORIZED:
175 0 : gtr.hr.ec = TALER_JSON_get_error_code (json);
176 0 : gtr.hr.hint = TALER_JSON_get_error_hint (json);
177 : /* Nothing really to verify, merchant says we need to authenticate. */
178 0 : break;
179 0 : case MHD_HTTP_NOT_FOUND:
180 : /* Nothing really to verify, this should never
181 : happen, we should pass the JSON reply to the application */
182 0 : gtr.hr.ec = TALER_JSON_get_error_code (json);
183 0 : gtr.hr.hint = TALER_JSON_get_error_hint (json);
184 0 : break;
185 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
186 : /* Server had an internal issue; we should retry, but this API
187 : leaves this to the application */
188 0 : gtr.hr.ec = TALER_JSON_get_error_code (json);
189 0 : gtr.hr.hint = TALER_JSON_get_error_hint (json);
190 0 : break;
191 0 : default:
192 : /* unexpected response code */
193 0 : GNUNET_break_op (0);
194 0 : TALER_MERCHANT_parse_error_details_ (json,
195 : response_code,
196 : >r.hr);
197 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
198 : "Unexpected response code %u/%d\n",
199 : (unsigned int) response_code,
200 : (int) gtr.hr.ec);
201 0 : gtr.hr.http_status = 0;
202 0 : break;
203 : }
204 0 : gth->cb (gth->cb_cls,
205 : >r);
206 0 : TALER_MERCHANT_transfers_get_cancel (gth);
207 : }
208 :
209 :
210 : struct TALER_MERCHANT_GetTransfersHandle *
211 4 : TALER_MERCHANT_transfers_get (
212 : struct GNUNET_CURL_Context *ctx,
213 : const char *backend_url,
214 : struct TALER_FullPayto payto_uri,
215 : const struct GNUNET_TIME_Timestamp before,
216 : const struct GNUNET_TIME_Timestamp after,
217 : int64_t limit,
218 : uint64_t offset,
219 : enum TALER_EXCHANGE_YesNoAll expected,
220 : TALER_MERCHANT_GetTransfersCallback cb,
221 : void *cb_cls)
222 : {
223 : struct TALER_MERCHANT_GetTransfersHandle *gth;
224 : CURL *eh;
225 4 : const char *expected_s = NULL;
226 : char limit_s[30];
227 : char offset_s[30];
228 : char before_s[30];
229 : char after_s[30];
230 :
231 4 : gth = GNUNET_new (struct TALER_MERCHANT_GetTransfersHandle);
232 4 : gth->ctx = ctx;
233 4 : gth->cb = cb;
234 4 : gth->cb_cls = cb_cls;
235 4 : expected_s = TALER_yna_to_string (expected);
236 4 : GNUNET_snprintf (limit_s,
237 : sizeof (limit_s),
238 : "%lld",
239 : (long long) limit);
240 4 : GNUNET_snprintf (offset_s,
241 : sizeof (offset_s),
242 : "%lld",
243 : (unsigned long long) offset);
244 4 : GNUNET_snprintf (before_s,
245 : sizeof (before_s),
246 : "%llu",
247 4 : (unsigned long long) GNUNET_TIME_timestamp_to_s (before));
248 4 : GNUNET_snprintf (after_s,
249 : sizeof (after_s),
250 : "%llu",
251 4 : (unsigned long long) GNUNET_TIME_timestamp_to_s (after));
252 : {
253 4 : char *enc_payto = TALER_urlencode (payto_uri.full_payto);
254 :
255 8 : gth->url = TALER_url_join (backend_url,
256 : "private/transfers",
257 : "payto_uri",
258 : enc_payto,
259 : "expected",
260 : (TALER_EXCHANGE_YNA_ALL != expected)
261 : ? expected_s
262 : : NULL,
263 : "limit",
264 : 0 != limit
265 : ? limit_s
266 : : NULL,
267 : "offset",
268 0 : ((0 != offset) && (UINT64_MAX != offset))
269 : ? offset_s
270 : : NULL,
271 : "before",
272 4 : GNUNET_TIME_absolute_is_never (before.abs_time)
273 : ? NULL
274 : : before_s,
275 : "after",
276 4 : GNUNET_TIME_absolute_is_zero (after.abs_time)
277 : ? NULL
278 : : after_s,
279 : NULL);
280 4 : GNUNET_free (enc_payto);
281 : }
282 4 : if (NULL == gth->url)
283 : {
284 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
285 : "Could not construct request URL.\n");
286 0 : GNUNET_free (gth);
287 0 : return NULL;
288 : }
289 4 : eh = TALER_MERCHANT_curl_easy_get_ (gth->url);
290 4 : gth->job = GNUNET_CURL_job_add (ctx,
291 : eh,
292 : &handle_transfers_get_finished,
293 : gth);
294 4 : return gth;
295 : }
296 :
297 :
298 : void
299 4 : TALER_MERCHANT_transfers_get_cancel (
300 : struct TALER_MERCHANT_GetTransfersHandle *gth)
301 : {
302 4 : if (NULL != gth->job)
303 : {
304 0 : GNUNET_CURL_job_cancel (gth->job);
305 0 : gth->job = NULL;
306 : }
307 4 : GNUNET_free (gth->url);
308 4 : GNUNET_free (gth);
309 4 : }
310 :
311 :
312 : /* end of merchant_api_get_transfers.c */
|