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