Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2015-2023 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_kyc_info.c
19 : * @brief Implementation of the /kyc-info/$AT request
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include <microhttpd.h> /* just for HTTP status codes */
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <gnunet/gnunet_curl_lib.h>
26 : #include "taler_exchange_service.h"
27 : #include "taler_json_lib.h"
28 : #include "exchange_api_handle.h"
29 : #include "taler_signatures.h"
30 : #include "exchange_api_curl_defaults.h"
31 :
32 :
33 : /**
34 : * @brief A /kyc-info Handle
35 : */
36 : struct TALER_EXCHANGE_KycInfoHandle
37 : {
38 :
39 : /**
40 : * The url for this request.
41 : */
42 : char *url;
43 :
44 : /**
45 : * Handle for the request.
46 : */
47 : struct GNUNET_CURL_Job *job;
48 :
49 : /**
50 : * Function to call with the result.
51 : */
52 : TALER_EXCHANGE_KycInfoCallback kyc_info_cb;
53 :
54 : /**
55 : * Closure for @e cb.
56 : */
57 : void *kyc_info_cb_cls;
58 :
59 : };
60 :
61 :
62 : /**
63 : * Parse the provided kyc_infoage data from the "200 OK" response
64 : * for one of the coins.
65 : *
66 : * @param[in,out] lh kyc_info handle (callback may be zero'ed out)
67 : * @param json json reply with the data for one coin
68 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
69 : */
70 : static enum GNUNET_GenericReturnValue
71 11 : parse_kyc_info_ok (struct TALER_EXCHANGE_KycInfoHandle *lh,
72 : const json_t *json)
73 : {
74 11 : const json_t *jrequirements = NULL;
75 11 : const json_t *jvoluntary_checks = NULL;
76 11 : struct TALER_EXCHANGE_KycProcessClientInformation lr = {
77 : .hr.reply = json,
78 : .hr.http_status = MHD_HTTP_OK
79 : };
80 : struct GNUNET_JSON_Specification spec[] = {
81 11 : GNUNET_JSON_spec_array_const ("requirements",
82 : &jrequirements),
83 11 : GNUNET_JSON_spec_bool ("is_and_combinator",
84 : &lr.details.ok.is_and_combinator),
85 11 : GNUNET_JSON_spec_mark_optional (
86 : GNUNET_JSON_spec_object_const ("voluntary_checks",
87 : &jvoluntary_checks),
88 : NULL),
89 11 : GNUNET_JSON_spec_end ()
90 : };
91 :
92 11 : if (GNUNET_OK !=
93 11 : GNUNET_JSON_parse (json,
94 : spec,
95 : NULL, NULL))
96 : {
97 0 : GNUNET_break_op (0);
98 0 : return GNUNET_SYSERR;
99 : }
100 :
101 : lr.details.ok.vci_length
102 11 : = (unsigned int) json_object_size (jvoluntary_checks);
103 11 : if ( ((size_t) lr.details.ok.vci_length)
104 11 : != json_object_size (jvoluntary_checks))
105 : {
106 0 : GNUNET_break_op (0);
107 0 : return GNUNET_SYSERR;
108 : }
109 : lr.details.ok.requirements_length
110 11 : = json_array_size (jrequirements);
111 11 : if ( ((size_t) lr.details.ok.requirements_length)
112 11 : != json_array_size (jrequirements))
113 : {
114 0 : GNUNET_break_op (0);
115 0 : return GNUNET_SYSERR;
116 : }
117 :
118 11 : {
119 11 : struct TALER_EXCHANGE_VoluntaryCheckInformation vci[
120 11 : GNUNET_NZL (lr.details.ok.vci_length)];
121 11 : struct TALER_EXCHANGE_RequirementInformation requirements[
122 11 : GNUNET_NZL (lr.details.ok.requirements_length)];
123 : const char *name;
124 : const json_t *jreq;
125 : const json_t *jvc;
126 : size_t off;
127 :
128 11 : memset (vci,
129 : 0,
130 : sizeof (vci));
131 11 : memset (requirements,
132 : 0,
133 : sizeof (requirements));
134 :
135 22 : json_array_foreach ((json_t *) jrequirements, off, jreq)
136 : {
137 11 : struct TALER_EXCHANGE_RequirementInformation *req = &requirements[off];
138 : struct GNUNET_JSON_Specification ispec[] = {
139 11 : GNUNET_JSON_spec_string ("form",
140 : &req->form),
141 11 : GNUNET_JSON_spec_string ("description",
142 : &req->description),
143 11 : GNUNET_JSON_spec_mark_optional (
144 : GNUNET_JSON_spec_object_const ("description_i18n",
145 : &req->description_i18n),
146 : NULL),
147 11 : GNUNET_JSON_spec_mark_optional (
148 : GNUNET_JSON_spec_string ("id",
149 : &req->id),
150 : NULL),
151 11 : GNUNET_JSON_spec_end ()
152 : };
153 :
154 11 : if (GNUNET_OK !=
155 11 : GNUNET_JSON_parse (jreq,
156 : ispec,
157 : NULL, NULL))
158 : {
159 0 : GNUNET_break_op (0);
160 0 : return GNUNET_SYSERR;
161 : }
162 : }
163 11 : GNUNET_assert (off == lr.details.ok.requirements_length);
164 :
165 11 : off = 0;
166 11 : json_object_foreach ((json_t *) jvoluntary_checks, name, jvc)
167 : {
168 0 : struct TALER_EXCHANGE_VoluntaryCheckInformation *vc = &vci[off++];
169 : struct GNUNET_JSON_Specification ispec[] = {
170 0 : GNUNET_JSON_spec_string ("description",
171 : &vc->description),
172 0 : GNUNET_JSON_spec_mark_optional (
173 : GNUNET_JSON_spec_object_const ("description_i18n",
174 : &vc->description_i18n),
175 : NULL),
176 0 : GNUNET_JSON_spec_end ()
177 : };
178 :
179 0 : vc->name = name;
180 0 : if (GNUNET_OK !=
181 0 : GNUNET_JSON_parse (jvc,
182 : ispec,
183 : NULL, NULL))
184 : {
185 0 : GNUNET_break_op (0);
186 0 : return GNUNET_SYSERR;
187 : }
188 : }
189 11 : GNUNET_assert (off == lr.details.ok.vci_length);
190 :
191 11 : lr.details.ok.vci = vci;
192 11 : lr.details.ok.requirements = requirements;
193 11 : lh->kyc_info_cb (lh->kyc_info_cb_cls,
194 : &lr);
195 11 : lh->kyc_info_cb = NULL;
196 11 : return GNUNET_OK;
197 : }
198 : }
199 :
200 :
201 : /**
202 : * Function called when we're done processing the
203 : * HTTP /kyc-info/$AT request.
204 : *
205 : * @param cls the `struct TALER_EXCHANGE_KycInfoHandle`
206 : * @param response_code HTTP response code, 0 on error
207 : * @param response parsed JSON result, NULL on error
208 : */
209 : static void
210 11 : handle_kyc_info_finished (void *cls,
211 : long response_code,
212 : const void *response)
213 : {
214 11 : struct TALER_EXCHANGE_KycInfoHandle *lh = cls;
215 11 : const json_t *j = response;
216 11 : struct TALER_EXCHANGE_KycProcessClientInformation lr = {
217 : .hr.reply = j,
218 11 : .hr.http_status = (unsigned int) response_code
219 : };
220 :
221 11 : lh->job = NULL;
222 11 : switch (response_code)
223 : {
224 0 : case 0:
225 0 : lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
226 0 : break;
227 11 : case MHD_HTTP_OK:
228 11 : if (GNUNET_OK !=
229 11 : parse_kyc_info_ok (lh,
230 : j))
231 : {
232 0 : GNUNET_break_op (0);
233 0 : lr.hr.http_status = 0;
234 0 : lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
235 0 : break;
236 : }
237 11 : GNUNET_assert (NULL == lh->kyc_info_cb);
238 11 : TALER_EXCHANGE_kyc_info_cancel (lh);
239 11 : return;
240 0 : case MHD_HTTP_NO_CONTENT:
241 0 : break;
242 0 : case MHD_HTTP_BAD_REQUEST:
243 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
244 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
245 : /* This should never happen, either us or the exchange is buggy
246 : (or API version conflict); just pass JSON reply to the application */
247 0 : break;
248 0 : case MHD_HTTP_FORBIDDEN:
249 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
250 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
251 0 : break;
252 0 : case MHD_HTTP_NOT_FOUND:
253 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
254 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
255 : /* Nothing really to verify, exchange says this coin was not melted; we
256 : should pass the JSON reply to the application */
257 0 : break;
258 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
259 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
260 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
261 : /* Server had an internal issue; we should retry, but this API
262 : leaves this to the application */
263 0 : break;
264 0 : default:
265 : /* unexpected response code */
266 0 : GNUNET_break_op (0);
267 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
268 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
269 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270 : "Unexpected response code %u/%d for exchange /kyc-info\n",
271 : (unsigned int) response_code,
272 : (int) lr.hr.ec);
273 0 : break;
274 : }
275 0 : if (NULL != lh->kyc_info_cb)
276 0 : lh->kyc_info_cb (lh->kyc_info_cb_cls,
277 : &lr);
278 0 : TALER_EXCHANGE_kyc_info_cancel (lh);
279 : }
280 :
281 :
282 : struct TALER_EXCHANGE_KycInfoHandle *
283 11 : TALER_EXCHANGE_kyc_info (
284 : struct GNUNET_CURL_Context *ctx,
285 : const char *url,
286 : const struct TALER_AccountAccessTokenP *token,
287 : const char *if_none_match,
288 : struct GNUNET_TIME_Relative timeout,
289 : TALER_EXCHANGE_KycInfoCallback cb,
290 : void *cb_cls)
291 : {
292 : struct TALER_EXCHANGE_KycInfoHandle *lh;
293 : CURL *eh;
294 : char arg_str[sizeof (struct TALER_AccountAccessTokenP) * 2 + 32];
295 11 : unsigned int tms
296 11 : = (unsigned int) timeout.rel_value_us
297 11 : / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
298 11 : struct curl_slist *job_headers = NULL;
299 :
300 : {
301 : char at_str[sizeof (struct TALER_AccountAccessTokenP) * 2];
302 : char *end;
303 :
304 11 : end = GNUNET_STRINGS_data_to_string (
305 : token,
306 : sizeof (*token),
307 : at_str,
308 : sizeof (at_str));
309 11 : *end = '\0';
310 11 : GNUNET_snprintf (arg_str,
311 : sizeof (arg_str),
312 : "kyc-info/%s",
313 : at_str);
314 : }
315 11 : lh = GNUNET_new (struct TALER_EXCHANGE_KycInfoHandle);
316 11 : lh->kyc_info_cb = cb;
317 11 : lh->kyc_info_cb_cls = cb_cls;
318 : {
319 : char timeout_str[32];
320 :
321 11 : GNUNET_snprintf (timeout_str,
322 : sizeof (timeout_str),
323 : "%u",
324 : tms);
325 11 : lh->url = TALER_url_join (url,
326 : arg_str,
327 : "timeout_ms",
328 : (0 == tms)
329 : ? NULL
330 : : timeout_str,
331 : NULL);
332 : }
333 11 : if (NULL == lh->url)
334 : {
335 0 : GNUNET_free (lh);
336 0 : return NULL;
337 : }
338 11 : eh = TALER_EXCHANGE_curl_easy_get_ (lh->url);
339 11 : if (NULL == eh)
340 : {
341 0 : GNUNET_break (0);
342 0 : GNUNET_free (lh->url);
343 0 : GNUNET_free (lh);
344 0 : return NULL;
345 : }
346 11 : if (0 != tms)
347 : {
348 0 : GNUNET_break (CURLE_OK ==
349 : curl_easy_setopt (eh,
350 : CURLOPT_TIMEOUT_MS,
351 : (long) (tms + 100L)));
352 : }
353 :
354 11 : job_headers = curl_slist_append (job_headers,
355 : "Content-Type: application/json");
356 11 : if (NULL != if_none_match)
357 : {
358 : char *hdr;
359 :
360 0 : GNUNET_asprintf (&hdr,
361 : "%s: %s",
362 : MHD_HTTP_HEADER_IF_NONE_MATCH,
363 : if_none_match);
364 0 : job_headers = curl_slist_append (job_headers,
365 : hdr);
366 0 : GNUNET_free (hdr);
367 : }
368 11 : lh->job = GNUNET_CURL_job_add2 (ctx,
369 : eh,
370 : job_headers,
371 : &handle_kyc_info_finished,
372 : lh);
373 11 : curl_slist_free_all (job_headers);
374 11 : return lh;
375 : }
376 :
377 :
378 : void
379 11 : TALER_EXCHANGE_kyc_info_cancel (struct TALER_EXCHANGE_KycInfoHandle *lh)
380 : {
381 11 : if (NULL != lh->job)
382 : {
383 0 : GNUNET_CURL_job_cancel (lh->job);
384 0 : lh->job = NULL;
385 : }
386 :
387 11 : GNUNET_free (lh->url);
388 11 : GNUNET_free (lh);
389 11 : }
390 :
391 :
392 : /* end of exchange_api_kyc_info.c */
|