Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2015-2026 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 lib/exchange_api_post-management-wire.c
19 : * @brief functions to enable an exchange wire method / bank account
20 : * @author Christian Grothoff
21 : */
22 : #include "taler/platform.h"
23 : #include "taler/taler_json_lib.h"
24 : #include <gnunet/gnunet_curl_lib.h>
25 : #include <microhttpd.h>
26 : #include "taler/taler_exchange_service.h"
27 : #include "taler/taler-exchange/post-management-wire.h"
28 : #include "exchange_api_curl_defaults.h"
29 : #include "taler/taler_signatures.h"
30 : #include "taler/taler_curl_lib.h"
31 :
32 :
33 : struct TALER_EXCHANGE_PostManagementWireHandle
34 : {
35 :
36 : /**
37 : * The base URL for this request.
38 : */
39 : char *base_url;
40 :
41 : /**
42 : * The full URL for this request, set during _start.
43 : */
44 : char *url;
45 :
46 : /**
47 : * Minor context that holds body and headers.
48 : */
49 : struct TALER_CURL_PostContext post_ctx;
50 :
51 : /**
52 : * Handle for the request.
53 : */
54 : struct GNUNET_CURL_Job *job;
55 :
56 : /**
57 : * Function to call with the result.
58 : */
59 : TALER_EXCHANGE_PostManagementWireCallback cb;
60 :
61 : /**
62 : * Closure for @a cb.
63 : */
64 : TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls;
65 :
66 : /**
67 : * Reference to the execution context.
68 : */
69 : struct GNUNET_CURL_Context *ctx;
70 :
71 : /**
72 : * Payto URI of the exchange's bank account.
73 : */
74 : char *payto_uri_str;
75 :
76 : /**
77 : * URL of the conversion service, or NULL.
78 : */
79 : char *conversion_url;
80 :
81 : /**
82 : * URL of the open banking gateway, or NULL.
83 : */
84 : char *open_banking_gateway;
85 :
86 : /**
87 : * URL of the wire transfer gateway, or NULL.
88 : */
89 : char *wire_transfer_gateway;
90 :
91 : /**
92 : * JSON encoding of debit restrictions (we hold a reference).
93 : */
94 : json_t *debit_restrictions;
95 :
96 : /**
97 : * JSON encoding of credit restrictions (we hold a reference).
98 : */
99 : json_t *credit_restrictions;
100 :
101 : /**
102 : * When was this decided?
103 : */
104 : struct GNUNET_TIME_Timestamp validity_start;
105 :
106 : /**
107 : * Signature affirming the wire addition.
108 : */
109 : struct TALER_MasterSignatureP master_sig1;
110 :
111 : /**
112 : * Signature affirming the validity of the account for clients.
113 : */
114 : struct TALER_MasterSignatureP master_sig2;
115 :
116 : /**
117 : * Label to use when showing the account to users (or NULL).
118 : */
119 : char *bank_label;
120 :
121 : /**
122 : * Priority for ordering the bank accounts.
123 : */
124 : int64_t priority;
125 :
126 : };
127 :
128 :
129 : /**
130 : * Function called when we're done processing the
131 : * HTTP POST /management/wire request.
132 : *
133 : * @param cls the `struct TALER_EXCHANGE_PostManagementWireHandle`
134 : * @param response_code HTTP response code, 0 on error
135 : * @param response response body, NULL if not in JSON
136 : */
137 : static void
138 25 : handle_wire_finished (void *cls,
139 : long response_code,
140 : const void *response)
141 : {
142 25 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh = cls;
143 25 : const json_t *json = response;
144 25 : struct TALER_EXCHANGE_PostManagementWireResponse res = {
145 25 : .hr.http_status = (unsigned int) response_code,
146 : .hr.reply = json
147 : };
148 :
149 25 : pmwh->job = NULL;
150 25 : switch (response_code)
151 : {
152 0 : case 0:
153 : /* no reply */
154 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
155 0 : res.hr.hint = "server offline?";
156 0 : break;
157 23 : case MHD_HTTP_NO_CONTENT:
158 23 : break;
159 0 : case MHD_HTTP_BAD_REQUEST:
160 0 : res.hr.ec = TALER_JSON_get_error_code (json);
161 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
162 0 : break;
163 2 : case MHD_HTTP_FORBIDDEN:
164 2 : res.hr.ec = TALER_JSON_get_error_code (json);
165 2 : res.hr.hint = TALER_JSON_get_error_hint (json);
166 2 : break;
167 0 : case MHD_HTTP_CONFLICT:
168 0 : res.hr.ec = TALER_JSON_get_error_code (json);
169 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
170 0 : break;
171 0 : default:
172 : /* unexpected response code */
173 0 : GNUNET_break_op (0);
174 0 : res.hr.ec = TALER_JSON_get_error_code (json);
175 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
176 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
177 : "Unexpected response code %u/%d for exchange management enable wire\n",
178 : (unsigned int) response_code,
179 : (int) res.hr.ec);
180 0 : break;
181 : }
182 25 : if (NULL != pmwh->cb)
183 : {
184 25 : pmwh->cb (pmwh->cb_cls,
185 : &res);
186 25 : pmwh->cb = NULL;
187 : }
188 25 : TALER_EXCHANGE_post_management_wire_cancel (pmwh);
189 25 : }
190 :
191 :
192 : struct TALER_EXCHANGE_PostManagementWireHandle *
193 25 : TALER_EXCHANGE_post_management_wire_create (
194 : struct GNUNET_CURL_Context *ctx,
195 : const char *url,
196 : const struct TALER_FullPayto payto_uri,
197 : struct GNUNET_TIME_Timestamp validity_start,
198 : const struct TALER_MasterSignatureP *master_sig1,
199 : const struct TALER_MasterSignatureP *master_sig2)
200 : {
201 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh;
202 : char *msg;
203 :
204 25 : msg = TALER_payto_validate (payto_uri);
205 25 : if (NULL != msg)
206 : {
207 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
208 : "payto URI is malformed: %s\n",
209 : msg);
210 0 : GNUNET_free (msg);
211 0 : return NULL;
212 : }
213 25 : pmwh = GNUNET_new (struct TALER_EXCHANGE_PostManagementWireHandle);
214 25 : pmwh->ctx = ctx;
215 25 : pmwh->base_url = GNUNET_strdup (url);
216 25 : pmwh->payto_uri_str = GNUNET_strdup (payto_uri.full_payto);
217 25 : pmwh->debit_restrictions = json_array ();
218 25 : GNUNET_assert (NULL != pmwh->debit_restrictions);
219 25 : pmwh->credit_restrictions = json_array ();
220 25 : GNUNET_assert (NULL != pmwh->credit_restrictions);
221 25 : pmwh->validity_start = validity_start;
222 25 : pmwh->master_sig1 = *master_sig1;
223 25 : pmwh->master_sig2 = *master_sig2;
224 25 : return pmwh;
225 : }
226 :
227 :
228 : enum GNUNET_GenericReturnValue
229 25 : TALER_EXCHANGE_post_management_wire_set_options_ (
230 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh,
231 : unsigned int num_options,
232 : const struct TALER_EXCHANGE_PostManagementWireOptionValue options[])
233 : {
234 168 : for (unsigned int i = 0; i < num_options; i++)
235 : {
236 168 : const struct TALER_EXCHANGE_PostManagementWireOptionValue *opt
237 168 : = &options[i];
238 :
239 168 : switch (opt->option)
240 : {
241 25 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_END:
242 25 : return GNUNET_OK;
243 25 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_BANK_LABEL:
244 25 : GNUNET_free (pmwh->bank_label);
245 50 : pmwh->bank_label = (NULL != opt->details.bank_label)
246 8 : ? GNUNET_strdup (opt->details.bank_label)
247 25 : : NULL;
248 25 : break;
249 17 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_PRIORITY:
250 17 : pmwh->priority = opt->details.priority;
251 17 : break;
252 17 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_CONVERSION_URL:
253 17 : GNUNET_free (pmwh->conversion_url);
254 34 : pmwh->conversion_url = (NULL != opt->details.conversion_url)
255 0 : ? GNUNET_strdup (opt->details.conversion_url)
256 17 : : NULL;
257 17 : break;
258 17 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_OPEN_BANKING_GATEWAY:
259 17 : GNUNET_free (pmwh->open_banking_gateway);
260 34 : pmwh->open_banking_gateway = (NULL != opt->details.open_banking_gateway)
261 0 : ? GNUNET_strdup (opt->details.open_banking_gateway)
262 17 : : NULL;
263 17 : break;
264 17 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_WIRE_TRANSFER_GATEWAY:
265 17 : GNUNET_free (pmwh->wire_transfer_gateway);
266 34 : pmwh->wire_transfer_gateway = (NULL != opt->details.wire_transfer_gateway)
267 0 : ? GNUNET_strdup (opt->details.wire_transfer_gateway)
268 17 : : NULL;
269 17 : break;
270 25 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_CREDIT_RESTRICTIONS:
271 25 : json_decref (pmwh->credit_restrictions);
272 50 : pmwh->credit_restrictions = (NULL != opt->details.credit_restrictions)
273 25 : ? json_incref ((json_t *) opt->details.credit_restrictions)
274 25 : : json_array ();
275 25 : break;
276 25 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_DEBIT_RESTRICTIONS:
277 25 : json_decref (pmwh->debit_restrictions);
278 50 : pmwh->debit_restrictions = (NULL != opt->details.debit_restrictions)
279 25 : ? json_incref ((json_t *) opt->details.debit_restrictions)
280 25 : : json_array ();
281 25 : break;
282 0 : default:
283 0 : GNUNET_break (0);
284 0 : return GNUNET_SYSERR;
285 : }
286 : }
287 0 : return GNUNET_OK;
288 : }
289 :
290 :
291 : enum TALER_ErrorCode
292 25 : TALER_EXCHANGE_post_management_wire_start (
293 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh,
294 : TALER_EXCHANGE_PostManagementWireCallback cb,
295 : TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls)
296 : {
297 : CURL *eh;
298 : json_t *body;
299 25 : struct TALER_FullPayto payto_uri = {
300 25 : .full_payto = pmwh->payto_uri_str
301 : };
302 :
303 25 : pmwh->cb = cb;
304 25 : pmwh->cb_cls = cb_cls;
305 25 : pmwh->url = TALER_url_join (pmwh->base_url,
306 : "management/wire",
307 : NULL);
308 25 : if (NULL == pmwh->url)
309 : {
310 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
311 : "Could not construct request URL.\n");
312 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
313 : }
314 25 : body = GNUNET_JSON_PACK (
315 : TALER_JSON_pack_full_payto ("payto_uri",
316 : payto_uri),
317 : GNUNET_JSON_pack_array_incref ("debit_restrictions",
318 : pmwh->debit_restrictions),
319 : GNUNET_JSON_pack_array_incref ("credit_restrictions",
320 : pmwh->credit_restrictions),
321 : GNUNET_JSON_pack_allow_null (
322 : GNUNET_JSON_pack_string ("conversion_url",
323 : pmwh->conversion_url)),
324 : GNUNET_JSON_pack_allow_null (
325 : GNUNET_JSON_pack_string ("open_banking_gateway",
326 : pmwh->open_banking_gateway)),
327 : GNUNET_JSON_pack_allow_null (
328 : GNUNET_JSON_pack_string ("wire_transfer_gateway",
329 : pmwh->wire_transfer_gateway)),
330 : GNUNET_JSON_pack_allow_null (
331 : GNUNET_JSON_pack_string ("bank_label",
332 : pmwh->bank_label)),
333 : GNUNET_JSON_pack_int64 ("priority",
334 : pmwh->priority),
335 : GNUNET_JSON_pack_data_auto ("master_sig_add",
336 : &pmwh->master_sig1),
337 : GNUNET_JSON_pack_data_auto ("master_sig_wire",
338 : &pmwh->master_sig2),
339 : GNUNET_JSON_pack_timestamp ("validity_start",
340 : pmwh->validity_start));
341 25 : eh = TALER_EXCHANGE_curl_easy_get_ (pmwh->url);
342 50 : if ( (NULL == eh) ||
343 : (GNUNET_OK !=
344 25 : TALER_curl_easy_post (&pmwh->post_ctx,
345 : eh,
346 : body)) )
347 : {
348 0 : GNUNET_break (0);
349 0 : if (NULL != eh)
350 0 : curl_easy_cleanup (eh);
351 0 : json_decref (body);
352 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
353 : }
354 25 : json_decref (body);
355 25 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356 : "Requesting URL '%s'\n",
357 : pmwh->url);
358 50 : pmwh->job = GNUNET_CURL_job_add2 (pmwh->ctx,
359 : eh,
360 25 : pmwh->post_ctx.headers,
361 : &handle_wire_finished,
362 : pmwh);
363 25 : if (NULL == pmwh->job)
364 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
365 25 : return TALER_EC_NONE;
366 : }
367 :
368 :
369 : void
370 25 : TALER_EXCHANGE_post_management_wire_cancel (
371 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh)
372 : {
373 25 : if (NULL != pmwh->job)
374 : {
375 0 : GNUNET_CURL_job_cancel (pmwh->job);
376 0 : pmwh->job = NULL;
377 : }
378 25 : TALER_curl_easy_post_finished (&pmwh->post_ctx);
379 25 : json_decref (pmwh->debit_restrictions);
380 25 : json_decref (pmwh->credit_restrictions);
381 25 : GNUNET_free (pmwh->payto_uri_str);
382 25 : GNUNET_free (pmwh->conversion_url);
383 25 : GNUNET_free (pmwh->open_banking_gateway);
384 25 : GNUNET_free (pmwh->wire_transfer_gateway);
385 25 : GNUNET_free (pmwh->bank_label);
386 25 : GNUNET_free (pmwh->url);
387 25 : GNUNET_free (pmwh->base_url);
388 25 : GNUNET_free (pmwh);
389 25 : }
390 :
391 :
392 : /* end of exchange_api_post-management-wire.c */
|