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