Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2015--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 General Public License as published by the Free Software
7 : Foundation; either version 3, 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 General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file bank-lib/bank_api_transfer.c
19 : * @brief Implementation of the /transfer/ requests of the bank's HTTP API
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include "bank_api_common.h"
24 : #include <microhttpd.h> /* just for HTTP status codes */
25 : #include "taler_signatures.h"
26 : #include "taler_curl_lib.h"
27 : #include "taler_bank_service.h"
28 :
29 :
30 : GNUNET_NETWORK_STRUCT_BEGIN
31 :
32 : /**
33 : * Data structure serialized in the prepare stage.
34 : */
35 : struct WirePackP
36 : {
37 : /**
38 : * Random unique identifier for the request.
39 : */
40 : struct GNUNET_HashCode request_uid;
41 :
42 : /**
43 : * Amount to be transferred.
44 : */
45 : struct TALER_AmountNBO amount;
46 :
47 : /**
48 : * Wire transfer identifier to use.
49 : */
50 : struct TALER_WireTransferIdentifierRawP wtid;
51 :
52 : /**
53 : * Length of the payto:// URL of the target account,
54 : * including 0-terminator, in network byte order.
55 : */
56 : uint32_t account_len GNUNET_PACKED;
57 :
58 : /**
59 : * Length of the exchange's base URL,
60 : * including 0-terminator, in network byte order.
61 : */
62 : uint32_t exchange_url_len GNUNET_PACKED;
63 :
64 : };
65 :
66 : GNUNET_NETWORK_STRUCT_END
67 :
68 :
69 : void
70 5 : TALER_BANK_prepare_transfer (
71 : const char *destination_account_payto_uri,
72 : const struct TALER_Amount *amount,
73 : const char *exchange_base_url,
74 : const struct TALER_WireTransferIdentifierRawP *wtid,
75 : void **buf,
76 : size_t *buf_size)
77 : {
78 : struct WirePackP *wp;
79 5 : size_t d_len = strlen (destination_account_payto_uri) + 1;
80 5 : size_t u_len = strlen (exchange_base_url) + 1;
81 : char *end;
82 :
83 5 : if ( (d_len >= (size_t) GNUNET_MAX_MALLOC_CHECKED) ||
84 5 : (u_len >= (size_t) GNUNET_MAX_MALLOC_CHECKED) ||
85 5 : (d_len + u_len + sizeof (*wp) >= GNUNET_MAX_MALLOC_CHECKED) )
86 : {
87 0 : GNUNET_break (0); /* that's some long URL... */
88 0 : *buf = NULL;
89 0 : *buf_size = 0;
90 0 : return;
91 : }
92 5 : *buf_size = sizeof (*wp) + d_len + u_len;
93 5 : wp = GNUNET_malloc (*buf_size);
94 5 : GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
95 : &wp->request_uid);
96 5 : TALER_amount_hton (&wp->amount,
97 : amount);
98 5 : wp->wtid = *wtid;
99 5 : wp->account_len = htonl ((uint32_t) d_len);
100 5 : wp->exchange_url_len = htonl ((uint32_t) u_len);
101 5 : end = (char *) &wp[1];
102 5 : memcpy (end,
103 : destination_account_payto_uri,
104 : d_len);
105 5 : memcpy (end + d_len,
106 : exchange_base_url,
107 : u_len);
108 5 : *buf = (char *) wp;
109 : }
110 :
111 :
112 : /**
113 : * @brief Handle for an active wire transfer.
114 : */
115 : struct TALER_BANK_TransferHandle
116 : {
117 :
118 : /**
119 : * The url for this request.
120 : */
121 : char *request_url;
122 :
123 : /**
124 : * POST context.
125 : */
126 : struct TALER_CURL_PostContext post_ctx;
127 :
128 : /**
129 : * Handle for the request.
130 : */
131 : struct GNUNET_CURL_Job *job;
132 :
133 : /**
134 : * Function to call with the result.
135 : */
136 : TALER_BANK_TransferCallback cb;
137 :
138 : /**
139 : * Closure for @a cb.
140 : */
141 : void *cb_cls;
142 :
143 : };
144 :
145 :
146 : /**
147 : * Function called when we're done processing the
148 : * HTTP /transfer request.
149 : *
150 : * @param cls the `struct TALER_BANK_TransferHandle`
151 : * @param response_code HTTP response code, 0 on error
152 : * @param response parsed JSON result, NULL on error
153 : */
154 : static void
155 5 : handle_transfer_finished (void *cls,
156 : long response_code,
157 : const void *response)
158 : {
159 5 : struct TALER_BANK_TransferHandle *th = cls;
160 5 : const json_t *j = response;
161 5 : uint64_t row_id = UINT64_MAX;
162 5 : struct GNUNET_TIME_Timestamp timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
163 : enum TALER_ErrorCode ec;
164 :
165 5 : th->job = NULL;
166 5 : switch (response_code)
167 : {
168 1 : case 0:
169 1 : ec = TALER_EC_GENERIC_INVALID_RESPONSE;
170 1 : break;
171 4 : case MHD_HTTP_OK:
172 : {
173 : struct GNUNET_JSON_Specification spec[] = {
174 4 : GNUNET_JSON_spec_uint64 ("row_id",
175 : &row_id),
176 4 : GNUNET_JSON_spec_timestamp ("timestamp",
177 : ×tamp),
178 4 : GNUNET_JSON_spec_end ()
179 : };
180 :
181 4 : if (GNUNET_OK !=
182 4 : GNUNET_JSON_parse (j,
183 : spec,
184 : NULL, NULL))
185 : {
186 0 : GNUNET_break_op (0);
187 0 : response_code = 0;
188 0 : ec = TALER_EC_GENERIC_INVALID_RESPONSE;
189 0 : break;
190 : }
191 4 : ec = TALER_EC_NONE;
192 : }
193 4 : break;
194 0 : case MHD_HTTP_BAD_REQUEST:
195 : /* This should never happen, either us or the bank is buggy
196 : (or API version conflict); just pass JSON reply to the application */
197 0 : GNUNET_break_op (0);
198 0 : ec = TALER_JSON_get_error_code (j);
199 0 : break;
200 0 : case MHD_HTTP_UNAUTHORIZED:
201 : /* Nothing really to verify, bank says our credentials are
202 : invalid. We should pass the JSON reply to the application. */
203 0 : ec = TALER_JSON_get_error_code (j);
204 0 : break;
205 0 : case MHD_HTTP_NOT_FOUND:
206 : /* Nothing really to verify, endpoint wrong -- could be user unknown */
207 0 : ec = TALER_JSON_get_error_code (j);
208 0 : break;
209 0 : case MHD_HTTP_CONFLICT:
210 : /* Nothing really to verify. Server says we used the same transfer request
211 : UID before, but with different details. Should not happen if the user
212 : properly used #TALER_BANK_prepare_transfer() and our PRNG is not
213 : broken... */
214 0 : ec = TALER_JSON_get_error_code (j);
215 0 : break;
216 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
217 : /* Server had an internal issue; we should retry, but this API
218 : leaves this to the application */
219 0 : ec = TALER_JSON_get_error_code (j);
220 0 : break;
221 0 : default:
222 : /* unexpected response code */
223 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
224 : "Unexpected response code %u\n",
225 : (unsigned int) response_code);
226 0 : GNUNET_break (0);
227 0 : ec = TALER_JSON_get_error_code (j);
228 0 : break;
229 : }
230 5 : th->cb (th->cb_cls,
231 : response_code,
232 : ec,
233 : row_id,
234 : timestamp);
235 5 : TALER_BANK_transfer_cancel (th);
236 5 : }
237 :
238 :
239 : struct TALER_BANK_TransferHandle *
240 5 : TALER_BANK_transfer (
241 : struct GNUNET_CURL_Context *ctx,
242 : const struct TALER_BANK_AuthenticationData *auth,
243 : const void *buf,
244 : size_t buf_size,
245 : TALER_BANK_TransferCallback cc,
246 : void *cc_cls)
247 : {
248 : struct TALER_BANK_TransferHandle *th;
249 : json_t *transfer_obj;
250 : CURL *eh;
251 5 : const struct WirePackP *wp = buf;
252 : uint32_t d_len;
253 : uint32_t u_len;
254 : const char *destination_account_uri;
255 : const char *exchange_base_url;
256 : struct TALER_Amount amount;
257 :
258 5 : if (sizeof (*wp) > buf_size)
259 : {
260 0 : GNUNET_break (0);
261 0 : return NULL;
262 : }
263 5 : d_len = ntohl (wp->account_len);
264 5 : u_len = ntohl (wp->exchange_url_len);
265 5 : if ( (sizeof (*wp) + d_len + u_len != buf_size) ||
266 5 : (d_len > buf_size) ||
267 5 : (u_len > buf_size) ||
268 5 : (d_len + u_len > buf_size) )
269 : {
270 0 : GNUNET_break (0);
271 0 : return NULL;
272 : }
273 5 : destination_account_uri = (const char *) &wp[1];
274 5 : exchange_base_url = destination_account_uri + d_len;
275 5 : if ( ('\0' != destination_account_uri[d_len - 1]) ||
276 5 : ('\0' != exchange_base_url[u_len - 1]) )
277 : {
278 0 : GNUNET_break (0);
279 0 : return NULL;
280 : }
281 5 : if (NULL == auth->wire_gateway_url)
282 : {
283 0 : GNUNET_break (0);
284 0 : return NULL;
285 : }
286 5 : TALER_amount_ntoh (&amount,
287 : &wp->amount);
288 5 : th = GNUNET_new (struct TALER_BANK_TransferHandle);
289 5 : th->cb = cc;
290 5 : th->cb_cls = cc_cls;
291 5 : th->request_url = TALER_url_join (auth->wire_gateway_url,
292 : "transfer",
293 : NULL);
294 5 : if (NULL == th->request_url)
295 : {
296 0 : GNUNET_free (th);
297 0 : GNUNET_break (0);
298 0 : return NULL;
299 : }
300 5 : transfer_obj = GNUNET_JSON_PACK (
301 : GNUNET_JSON_pack_data_auto ("request_uid",
302 : &wp->request_uid),
303 : TALER_JSON_pack_amount ("amount",
304 : &amount),
305 : GNUNET_JSON_pack_string ("exchange_base_url",
306 : exchange_base_url),
307 : GNUNET_JSON_pack_data_auto ("wtid",
308 : &wp->wtid),
309 : GNUNET_JSON_pack_string ("credit_account",
310 : destination_account_uri));
311 5 : if (NULL == transfer_obj)
312 : {
313 0 : GNUNET_break (0);
314 0 : return NULL;
315 : }
316 5 : eh = curl_easy_init ();
317 10 : if ( (NULL == eh) ||
318 : (GNUNET_OK !=
319 5 : TALER_BANK_setup_auth_ (eh,
320 5 : auth)) ||
321 : (CURLE_OK !=
322 5 : curl_easy_setopt (eh,
323 : CURLOPT_URL,
324 5 : th->request_url)) ||
325 : (GNUNET_OK !=
326 5 : TALER_curl_easy_post (&th->post_ctx,
327 : eh,
328 : transfer_obj)) )
329 : {
330 0 : GNUNET_break (0);
331 0 : TALER_BANK_transfer_cancel (th);
332 0 : if (NULL != eh)
333 0 : curl_easy_cleanup (eh);
334 0 : json_decref (transfer_obj);
335 0 : return NULL;
336 : }
337 5 : json_decref (transfer_obj);
338 10 : th->job = GNUNET_CURL_job_add2 (ctx,
339 : eh,
340 5 : th->post_ctx.headers,
341 : &handle_transfer_finished,
342 : th);
343 5 : return th;
344 : }
345 :
346 :
347 : void
348 5 : TALER_BANK_transfer_cancel (struct TALER_BANK_TransferHandle *th)
349 : {
350 5 : if (NULL != th->job)
351 : {
352 0 : GNUNET_CURL_job_cancel (th->job);
353 0 : th->job = NULL;
354 : }
355 5 : TALER_curl_easy_post_finished (&th->post_ctx);
356 5 : GNUNET_free (th->request_url);
357 5 : GNUNET_free (th);
358 5 : }
359 :
360 :
361 : /* end of bank_api_transfer.c */
|