Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2021-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-kyc-wallet.c
19 : * @brief Implementation of the /kyc-wallet request
20 : * @author Christian Grothoff
21 : */
22 : #include <microhttpd.h> /* just for HTTP status codes */
23 : #include <gnunet/gnunet_util_lib.h>
24 : #include <gnunet/gnunet_curl_lib.h>
25 : #include "taler/taler_json_lib.h"
26 : #include "taler/exchange/post-kyc-wallet.h"
27 : #include "taler/taler_signatures.h"
28 : #include "exchange_api_curl_defaults.h"
29 : #include "taler/taler_curl_lib.h"
30 :
31 :
32 : /**
33 : * @brief A POST /kyc-wallet handle
34 : */
35 : struct TALER_EXCHANGE_PostKycWalletHandle
36 : {
37 :
38 : /**
39 : * Context for curl easy post. Keeps the data that must
40 : * persist for Curl to make the upload.
41 : */
42 : struct TALER_CURL_PostContext post_ctx;
43 :
44 : /**
45 : * The base URL for this request.
46 : */
47 : char *base_url;
48 :
49 : /**
50 : * The full URL for this request, set during _start.
51 : */
52 : char *url;
53 :
54 : /**
55 : * Handle for the request.
56 : */
57 : struct GNUNET_CURL_Job *job;
58 :
59 : /**
60 : * Function to call with the result.
61 : */
62 : TALER_EXCHANGE_PostKycWalletCallback cb;
63 :
64 : /**
65 : * Closure for @e cb.
66 : */
67 : TALER_EXCHANGE_POST_KYC_WALLET_RESULT_CLOSURE *cb_cls;
68 :
69 : /**
70 : * Reference to the execution context.
71 : */
72 : struct GNUNET_CURL_Context *ctx;
73 :
74 : /**
75 : * Reserve private key of the wallet.
76 : */
77 : struct TALER_ReservePrivateKeyP reserve_priv;
78 :
79 : /**
80 : * Balance (or balance threshold) crossed by the wallet.
81 : */
82 : struct TALER_Amount balance;
83 :
84 : };
85 :
86 :
87 : /**
88 : * Function called when we're done processing the
89 : * HTTP /kyc-wallet request.
90 : *
91 : * @param cls the `struct TALER_EXCHANGE_PostKycWalletHandle`
92 : * @param response_code HTTP response code, 0 on error
93 : * @param response parsed JSON result, NULL on error
94 : */
95 : static void
96 7 : handle_kyc_wallet_finished (void *cls,
97 : long response_code,
98 : const void *response)
99 : {
100 7 : struct TALER_EXCHANGE_PostKycWalletHandle *pkwh = cls;
101 7 : const json_t *j = response;
102 7 : struct TALER_EXCHANGE_PostKycWalletResponse ks = {
103 : .hr.reply = j,
104 7 : .hr.http_status = (unsigned int) response_code
105 : };
106 :
107 7 : pkwh->job = NULL;
108 7 : switch (response_code)
109 : {
110 0 : case 0:
111 0 : ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
112 0 : break;
113 2 : case MHD_HTTP_OK:
114 : {
115 : struct GNUNET_JSON_Specification spec[] = {
116 2 : GNUNET_JSON_spec_mark_optional (
117 : TALER_JSON_spec_amount_any (
118 : "next_threshold",
119 : &ks.details.ok.next_threshold),
120 : NULL),
121 2 : GNUNET_JSON_spec_timestamp (
122 : "expiration_time",
123 : &ks.details.ok.expiration_time),
124 2 : GNUNET_JSON_spec_end ()
125 : };
126 :
127 2 : if (GNUNET_OK !=
128 2 : GNUNET_JSON_parse (j,
129 : spec,
130 : NULL, NULL))
131 : {
132 0 : GNUNET_break_op (0);
133 0 : ks.hr.http_status = 0;
134 0 : ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
135 0 : break;
136 : }
137 2 : break;
138 : }
139 0 : case MHD_HTTP_NO_CONTENT:
140 0 : break;
141 0 : case MHD_HTTP_BAD_REQUEST:
142 0 : ks.hr.ec = TALER_JSON_get_error_code (j);
143 : /* This should never happen, either us or the exchange is buggy
144 : (or API version conflict); just pass JSON reply to the application */
145 0 : break;
146 0 : case MHD_HTTP_FORBIDDEN:
147 0 : ks.hr.ec = TALER_JSON_get_error_code (j);
148 0 : break;
149 5 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
150 5 : ks.hr.ec = TALER_JSON_get_error_code (j);
151 5 : ks.hr.hint = TALER_JSON_get_error_hint (j);
152 5 : if (GNUNET_OK !=
153 5 : TALER_EXCHANGE_parse_451 (&ks.details.unavailable_for_legal_reasons,
154 : j))
155 : {
156 0 : GNUNET_break_op (0);
157 0 : ks.hr.http_status = 0;
158 0 : ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
159 0 : break;
160 : }
161 5 : break;
162 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
163 0 : ks.hr.ec = TALER_JSON_get_error_code (j);
164 : /* Server had an internal issue; we should retry, but this API
165 : leaves this to the application */
166 0 : break;
167 0 : default:
168 : /* unexpected response code */
169 0 : GNUNET_break_op (0);
170 0 : ks.hr.ec = TALER_JSON_get_error_code (j);
171 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
172 : "Unexpected response code %u/%d for exchange /kyc-wallet\n",
173 : (unsigned int) response_code,
174 : (int) ks.hr.ec);
175 0 : break;
176 : }
177 7 : if (NULL != pkwh->cb)
178 : {
179 7 : pkwh->cb (pkwh->cb_cls,
180 : &ks);
181 7 : pkwh->cb = NULL;
182 : }
183 7 : TALER_EXCHANGE_post_kyc_wallet_cancel (pkwh);
184 7 : }
185 :
186 :
187 : struct TALER_EXCHANGE_PostKycWalletHandle *
188 7 : TALER_EXCHANGE_post_kyc_wallet_create (
189 : struct GNUNET_CURL_Context *ctx,
190 : const char *url,
191 : const struct TALER_ReservePrivateKeyP *reserve_priv,
192 : const struct TALER_Amount *balance)
193 : {
194 : struct TALER_EXCHANGE_PostKycWalletHandle *pkwh;
195 :
196 7 : pkwh = GNUNET_new (struct TALER_EXCHANGE_PostKycWalletHandle);
197 7 : pkwh->ctx = ctx;
198 7 : pkwh->base_url = GNUNET_strdup (url);
199 7 : pkwh->reserve_priv = *reserve_priv;
200 7 : pkwh->balance = *balance;
201 7 : return pkwh;
202 : }
203 :
204 :
205 : enum TALER_ErrorCode
206 7 : TALER_EXCHANGE_post_kyc_wallet_start (
207 : struct TALER_EXCHANGE_PostKycWalletHandle *pkwh,
208 : TALER_EXCHANGE_PostKycWalletCallback cb,
209 : TALER_EXCHANGE_POST_KYC_WALLET_RESULT_CLOSURE *cb_cls)
210 : {
211 : CURL *eh;
212 : json_t *req;
213 : struct TALER_ReservePublicKeyP reserve_pub;
214 : struct TALER_ReserveSignatureP reserve_sig;
215 :
216 7 : pkwh->cb = cb;
217 7 : pkwh->cb_cls = cb_cls;
218 7 : GNUNET_CRYPTO_eddsa_key_get_public (&pkwh->reserve_priv.eddsa_priv,
219 : &reserve_pub.eddsa_pub);
220 7 : TALER_wallet_account_setup_sign (&pkwh->reserve_priv,
221 7 : &pkwh->balance,
222 : &reserve_sig);
223 7 : req = GNUNET_JSON_PACK (
224 : TALER_JSON_pack_amount ("balance",
225 : &pkwh->balance),
226 : GNUNET_JSON_pack_data_auto ("reserve_pub",
227 : &reserve_pub),
228 : GNUNET_JSON_pack_data_auto ("reserve_sig",
229 : &reserve_sig));
230 7 : GNUNET_assert (NULL != req);
231 7 : pkwh->url = TALER_url_join (pkwh->base_url,
232 : "kyc-wallet",
233 : NULL);
234 7 : if (NULL == pkwh->url)
235 : {
236 0 : json_decref (req);
237 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
238 : }
239 7 : eh = TALER_EXCHANGE_curl_easy_get_ (pkwh->url);
240 14 : if ( (NULL == eh) ||
241 : (GNUNET_OK !=
242 7 : TALER_curl_easy_post (&pkwh->post_ctx,
243 : eh,
244 : req)) )
245 : {
246 0 : GNUNET_break (0);
247 0 : if (NULL != eh)
248 0 : curl_easy_cleanup (eh);
249 0 : json_decref (req);
250 0 : GNUNET_free (pkwh->url);
251 0 : pkwh->url = NULL;
252 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
253 : }
254 7 : json_decref (req);
255 7 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
256 : "Requesting URL '%s'\n",
257 : pkwh->url);
258 14 : pkwh->job = GNUNET_CURL_job_add2 (pkwh->ctx,
259 : eh,
260 7 : pkwh->post_ctx.headers,
261 : &handle_kyc_wallet_finished,
262 : pkwh);
263 7 : if (NULL == pkwh->job)
264 : {
265 0 : TALER_curl_easy_post_finished (&pkwh->post_ctx);
266 0 : GNUNET_free (pkwh->url);
267 0 : pkwh->url = NULL;
268 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
269 : }
270 7 : return TALER_EC_NONE;
271 : }
272 :
273 :
274 : void
275 7 : TALER_EXCHANGE_post_kyc_wallet_cancel (
276 : struct TALER_EXCHANGE_PostKycWalletHandle *pkwh)
277 : {
278 7 : if (NULL != pkwh->job)
279 : {
280 0 : GNUNET_CURL_job_cancel (pkwh->job);
281 0 : pkwh->job = NULL;
282 : }
283 7 : TALER_curl_easy_post_finished (&pkwh->post_ctx);
284 7 : GNUNET_free (pkwh->url);
285 7 : GNUNET_free (pkwh->base_url);
286 7 : GNUNET_free (pkwh);
287 7 : }
288 :
289 :
290 : /* end of exchange_api_post-kyc-wallet.c */
|