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 : * JSON encoding of debit restrictions (we hold a reference).
83 : */
84 : json_t *debit_restrictions;
85 :
86 : /**
87 : * JSON encoding of credit restrictions (we hold a reference).
88 : */
89 : json_t *credit_restrictions;
90 :
91 : /**
92 : * When was this decided?
93 : */
94 : struct GNUNET_TIME_Timestamp validity_start;
95 :
96 : /**
97 : * Signature affirming the wire addition.
98 : */
99 : struct TALER_MasterSignatureP master_sig1;
100 :
101 : /**
102 : * Signature affirming the validity of the account for clients.
103 : */
104 : struct TALER_MasterSignatureP master_sig2;
105 :
106 : /**
107 : * Label to use when showing the account to users (or NULL).
108 : */
109 : char *bank_label;
110 :
111 : /**
112 : * Priority for ordering the bank accounts.
113 : */
114 : int64_t priority;
115 :
116 : };
117 :
118 :
119 : /**
120 : * Function called when we're done processing the
121 : * HTTP POST /management/wire request.
122 : *
123 : * @param cls the `struct TALER_EXCHANGE_PostManagementWireHandle`
124 : * @param response_code HTTP response code, 0 on error
125 : * @param response response body, NULL if not in JSON
126 : */
127 : static void
128 25 : handle_wire_finished (void *cls,
129 : long response_code,
130 : const void *response)
131 : {
132 25 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh = cls;
133 25 : const json_t *json = response;
134 25 : struct TALER_EXCHANGE_PostManagementWireResponse res = {
135 25 : .hr.http_status = (unsigned int) response_code,
136 : .hr.reply = json
137 : };
138 :
139 25 : pmwh->job = NULL;
140 25 : switch (response_code)
141 : {
142 0 : case 0:
143 : /* no reply */
144 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
145 0 : res.hr.hint = "server offline?";
146 0 : break;
147 23 : case MHD_HTTP_NO_CONTENT:
148 23 : break;
149 2 : case MHD_HTTP_FORBIDDEN:
150 2 : res.hr.ec = TALER_JSON_get_error_code (json);
151 2 : res.hr.hint = TALER_JSON_get_error_hint (json);
152 2 : break;
153 0 : case MHD_HTTP_NOT_FOUND:
154 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
155 : "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n",
156 : pmwh->url);
157 0 : if (NULL != json)
158 : {
159 0 : res.hr.ec = TALER_JSON_get_error_code (json);
160 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
161 : }
162 : else
163 : {
164 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
165 0 : res.hr.hint = TALER_ErrorCode_get_hint (res.hr.ec);
166 : }
167 0 : break;
168 0 : case MHD_HTTP_CONFLICT:
169 0 : res.hr.ec = TALER_JSON_get_error_code (json);
170 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
171 0 : break;
172 0 : default:
173 : /* unexpected response code */
174 0 : GNUNET_break_op (0);
175 0 : res.hr.ec = TALER_JSON_get_error_code (json);
176 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
177 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178 : "Unexpected response code %u/%d for exchange management enable wire\n",
179 : (unsigned int) response_code,
180 : (int) res.hr.ec);
181 0 : break;
182 : }
183 25 : if (NULL != pmwh->cb)
184 : {
185 25 : pmwh->cb (pmwh->cb_cls,
186 : &res);
187 25 : pmwh->cb = NULL;
188 : }
189 25 : TALER_EXCHANGE_post_management_wire_cancel (pmwh);
190 25 : }
191 :
192 :
193 : struct TALER_EXCHANGE_PostManagementWireHandle *
194 25 : TALER_EXCHANGE_post_management_wire_create (
195 : struct GNUNET_CURL_Context *ctx,
196 : const char *url,
197 : const struct TALER_FullPayto payto_uri,
198 : const char *conversion_url,
199 : const json_t *debit_restrictions,
200 : const json_t *credit_restrictions,
201 : struct GNUNET_TIME_Timestamp validity_start,
202 : const struct TALER_MasterSignatureP *master_sig1,
203 : const struct TALER_MasterSignatureP *master_sig2)
204 : {
205 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh;
206 : char *msg;
207 :
208 25 : msg = TALER_payto_validate (payto_uri);
209 25 : if (NULL != msg)
210 : {
211 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
212 : "payto URI is malformed: %s\n",
213 : msg);
214 0 : GNUNET_free (msg);
215 0 : return NULL;
216 : }
217 25 : pmwh = GNUNET_new (struct TALER_EXCHANGE_PostManagementWireHandle);
218 25 : pmwh->ctx = ctx;
219 25 : pmwh->base_url = GNUNET_strdup (url);
220 25 : pmwh->payto_uri_str = GNUNET_strdup (payto_uri.full_payto);
221 25 : pmwh->conversion_url = (NULL != conversion_url)
222 0 : ? GNUNET_strdup (conversion_url)
223 25 : : NULL;
224 25 : pmwh->debit_restrictions = json_incref ((json_t *) debit_restrictions);
225 25 : pmwh->credit_restrictions = json_incref ((json_t *) credit_restrictions);
226 25 : pmwh->validity_start = validity_start;
227 25 : pmwh->master_sig1 = *master_sig1;
228 25 : pmwh->master_sig2 = *master_sig2;
229 25 : return pmwh;
230 : }
231 :
232 :
233 : enum GNUNET_GenericReturnValue
234 17 : TALER_EXCHANGE_post_management_wire_set_options_ (
235 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh,
236 : unsigned int num_options,
237 : const struct TALER_EXCHANGE_PostManagementWireOptionValue options[])
238 : {
239 51 : for (unsigned int i = 0; i < num_options; i++)
240 : {
241 51 : const struct TALER_EXCHANGE_PostManagementWireOptionValue *opt
242 51 : = &options[i];
243 :
244 51 : switch (opt->option)
245 : {
246 17 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_END:
247 17 : return GNUNET_OK;
248 17 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_BANK_LABEL:
249 17 : GNUNET_free (pmwh->bank_label);
250 34 : pmwh->bank_label = (NULL != opt->details.bank_label)
251 0 : ? GNUNET_strdup (opt->details.bank_label)
252 17 : : NULL;
253 17 : break;
254 17 : case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_PRIORITY:
255 17 : pmwh->priority = opt->details.priority;
256 17 : break;
257 0 : default:
258 0 : GNUNET_break (0);
259 0 : return GNUNET_SYSERR;
260 : }
261 : }
262 0 : return GNUNET_OK;
263 : }
264 :
265 :
266 : enum TALER_ErrorCode
267 25 : TALER_EXCHANGE_post_management_wire_start (
268 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh,
269 : TALER_EXCHANGE_PostManagementWireCallback cb,
270 : TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls)
271 : {
272 : CURL *eh;
273 : json_t *body;
274 25 : struct TALER_FullPayto payto_uri = {
275 25 : .full_payto = pmwh->payto_uri_str
276 : };
277 :
278 25 : pmwh->cb = cb;
279 25 : pmwh->cb_cls = cb_cls;
280 25 : pmwh->url = TALER_url_join (pmwh->base_url,
281 : "management/wire",
282 : NULL);
283 25 : if (NULL == pmwh->url)
284 : {
285 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
286 : "Could not construct request URL.\n");
287 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
288 : }
289 25 : body = GNUNET_JSON_PACK (
290 : TALER_JSON_pack_full_payto ("payto_uri",
291 : payto_uri),
292 : GNUNET_JSON_pack_array_incref ("debit_restrictions",
293 : pmwh->debit_restrictions),
294 : GNUNET_JSON_pack_array_incref ("credit_restrictions",
295 : pmwh->credit_restrictions),
296 : GNUNET_JSON_pack_allow_null (
297 : GNUNET_JSON_pack_string ("conversion_url",
298 : pmwh->conversion_url)),
299 : GNUNET_JSON_pack_allow_null (
300 : GNUNET_JSON_pack_string ("bank_label",
301 : pmwh->bank_label)),
302 : GNUNET_JSON_pack_int64 ("priority",
303 : pmwh->priority),
304 : GNUNET_JSON_pack_data_auto ("master_sig_add",
305 : &pmwh->master_sig1),
306 : GNUNET_JSON_pack_data_auto ("master_sig_wire",
307 : &pmwh->master_sig2),
308 : GNUNET_JSON_pack_timestamp ("validity_start",
309 : pmwh->validity_start));
310 25 : eh = TALER_EXCHANGE_curl_easy_get_ (pmwh->url);
311 50 : if ( (NULL == eh) ||
312 : (GNUNET_OK !=
313 25 : TALER_curl_easy_post (&pmwh->post_ctx,
314 : eh,
315 : body)) )
316 : {
317 0 : GNUNET_break (0);
318 0 : if (NULL != eh)
319 0 : curl_easy_cleanup (eh);
320 0 : json_decref (body);
321 0 : GNUNET_free (pmwh->url);
322 0 : pmwh->url = NULL;
323 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
324 : }
325 25 : json_decref (body);
326 25 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327 : "Requesting URL '%s'\n",
328 : pmwh->url);
329 50 : pmwh->job = GNUNET_CURL_job_add2 (pmwh->ctx,
330 : eh,
331 25 : pmwh->post_ctx.headers,
332 : &handle_wire_finished,
333 : pmwh);
334 25 : if (NULL == pmwh->job)
335 : {
336 0 : TALER_curl_easy_post_finished (&pmwh->post_ctx);
337 0 : GNUNET_free (pmwh->url);
338 0 : pmwh->url = NULL;
339 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
340 : }
341 25 : return TALER_EC_NONE;
342 : }
343 :
344 :
345 : void
346 25 : TALER_EXCHANGE_post_management_wire_cancel (
347 : struct TALER_EXCHANGE_PostManagementWireHandle *pmwh)
348 : {
349 25 : if (NULL != pmwh->job)
350 : {
351 0 : GNUNET_CURL_job_cancel (pmwh->job);
352 0 : pmwh->job = NULL;
353 : }
354 25 : TALER_curl_easy_post_finished (&pmwh->post_ctx);
355 25 : json_decref (pmwh->debit_restrictions);
356 25 : json_decref (pmwh->credit_restrictions);
357 25 : GNUNET_free (pmwh->payto_uri_str);
358 25 : GNUNET_free (pmwh->conversion_url);
359 25 : GNUNET_free (pmwh->bank_label);
360 25 : GNUNET_free (pmwh->url);
361 25 : GNUNET_free (pmwh->base_url);
362 25 : GNUNET_free (pmwh);
363 25 : }
364 :
365 :
366 : /* end of exchange_api_post-management-wire.c */
|