diff options
Diffstat (limited to 'src/restclient/anastasis_api_truth_store.c')
-rw-r--r-- | src/restclient/anastasis_api_truth_store.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/src/restclient/anastasis_api_truth_store.c b/src/restclient/anastasis_api_truth_store.c new file mode 100644 index 0000000..ebd7d10 --- /dev/null +++ b/src/restclient/anastasis_api_truth_store.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /* | ||
2 | This file is part of Anastasis | ||
3 | Copyright (C) 2020, 2021 Taler Systems SA | ||
4 | |||
5 | Anastasis is free software; you can redistribute it and/or modify it under the | ||
6 | terms of the GNU Lesser General Public License as published by the Free Software | ||
7 | Foundation; either version 3, or (at your option) any later version. | ||
8 | |||
9 | Anastasis 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 | Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> | ||
15 | */ | ||
16 | /** | ||
17 | * @file lib/anastasis_api_truth_store.c | ||
18 | * @brief Implementation of the /truth GET and POST | ||
19 | * @author Christian Grothoff | ||
20 | * @author Dennis Neufeld | ||
21 | * @author Dominik Meister | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include <curl/curl.h> | ||
25 | #include <jansson.h> | ||
26 | #include <microhttpd.h> /* just for HTTP status codes */ | ||
27 | #include "anastasis_service.h" | ||
28 | #include "anastasis_api_curl_defaults.h" | ||
29 | #include <taler/taler_json_lib.h> | ||
30 | #include <taler/taler_merchant_service.h> | ||
31 | |||
32 | |||
33 | struct ANASTASIS_TruthStoreOperation | ||
34 | { | ||
35 | /** | ||
36 | * Complete URL where the backend offers /truth | ||
37 | */ | ||
38 | char *url; | ||
39 | |||
40 | /** | ||
41 | * Handle for the request. | ||
42 | */ | ||
43 | struct GNUNET_CURL_Job *job; | ||
44 | |||
45 | /** | ||
46 | * The CURL context to connect to the backend | ||
47 | */ | ||
48 | struct GNUNET_CURL_Context *ctx; | ||
49 | |||
50 | /** | ||
51 | * The callback to pass the backend response to | ||
52 | */ | ||
53 | ANASTASIS_TruthStoreCallback cb; | ||
54 | |||
55 | /** | ||
56 | * Closure for @e cb. | ||
57 | */ | ||
58 | void *cb_cls; | ||
59 | |||
60 | /** | ||
61 | * Reference to data (for cleanup). | ||
62 | */ | ||
63 | char *data; | ||
64 | |||
65 | /** | ||
66 | * Payment URI we received from the service, or NULL. | ||
67 | */ | ||
68 | char *pay_uri; | ||
69 | }; | ||
70 | |||
71 | |||
72 | void | ||
73 | ANASTASIS_truth_store_cancel ( | ||
74 | struct ANASTASIS_TruthStoreOperation *tso) | ||
75 | { | ||
76 | if (NULL != tso->job) | ||
77 | { | ||
78 | GNUNET_CURL_job_cancel (tso->job); | ||
79 | tso->job = NULL; | ||
80 | } | ||
81 | GNUNET_free (tso->pay_uri); | ||
82 | GNUNET_free (tso->url); | ||
83 | GNUNET_free (tso->data); | ||
84 | GNUNET_free (tso); | ||
85 | } | ||
86 | |||
87 | |||
88 | /** | ||
89 | * Callback to process POST /truth response | ||
90 | * | ||
91 | * @param cls the `struct ANASTASIS_TruthStoreOperation` | ||
92 | * @param response_code HTTP response code, 0 on error | ||
93 | * @param data | ||
94 | * @param data_size | ||
95 | */ | ||
96 | static void | ||
97 | handle_truth_store_finished (void *cls, | ||
98 | long response_code, | ||
99 | const void *data, | ||
100 | size_t data_size) | ||
101 | { | ||
102 | struct ANASTASIS_TruthStoreOperation *tso = cls; | ||
103 | struct ANASTASIS_UploadDetails ud; | ||
104 | |||
105 | tso->job = NULL; | ||
106 | memset (&ud, 0, sizeof (ud)); | ||
107 | ud.http_status = response_code; | ||
108 | ud.ec = TALER_EC_NONE; | ||
109 | switch (response_code) | ||
110 | { | ||
111 | case 0: | ||
112 | break; | ||
113 | case MHD_HTTP_NO_CONTENT: | ||
114 | ud.us = ANASTASIS_US_SUCCESS; | ||
115 | break; | ||
116 | case MHD_HTTP_NOT_MODIFIED: | ||
117 | ud.us = ANASTASIS_US_SUCCESS; | ||
118 | break; | ||
119 | case MHD_HTTP_BAD_REQUEST: | ||
120 | GNUNET_break (0); | ||
121 | ud.ec = TALER_JSON_get_error_code2 (data, | ||
122 | data_size); | ||
123 | break; | ||
124 | case MHD_HTTP_PAYMENT_REQUIRED: | ||
125 | { | ||
126 | struct TALER_MERCHANT_PayUriData pd; | ||
127 | |||
128 | if ( (NULL == tso->pay_uri) || | ||
129 | (GNUNET_OK != | ||
130 | TALER_MERCHANT_parse_pay_uri (tso->pay_uri, | ||
131 | &pd)) ) | ||
132 | { | ||
133 | GNUNET_break_op (0); | ||
134 | ud.ec = TALER_EC_ANASTASIS_GENERIC_INVALID_PAYMENT_REQUEST; | ||
135 | break; | ||
136 | } | ||
137 | if (GNUNET_OK != | ||
138 | GNUNET_STRINGS_string_to_data ( | ||
139 | pd.order_id, | ||
140 | strlen (pd.order_id), | ||
141 | &ud.details.payment.ps, | ||
142 | sizeof (ud.details.payment.ps))) | ||
143 | { | ||
144 | GNUNET_break (0); | ||
145 | ud.ec = TALER_EC_ANASTASIS_GENERIC_INVALID_PAYMENT_REQUEST; | ||
146 | TALER_MERCHANT_parse_pay_uri_free (&pd); | ||
147 | break; | ||
148 | } | ||
149 | TALER_MERCHANT_parse_pay_uri_free (&pd); | ||
150 | } | ||
151 | ud.us = ANASTASIS_US_PAYMENT_REQUIRED; | ||
152 | ud.details.payment.payment_request = tso->pay_uri; | ||
153 | break; | ||
154 | case MHD_HTTP_CONFLICT: | ||
155 | ud.us = ANASTASIS_US_CONFLICTING_TRUTH; | ||
156 | break; | ||
157 | case MHD_HTTP_LENGTH_REQUIRED: | ||
158 | GNUNET_break (0); | ||
159 | break; | ||
160 | case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE: | ||
161 | ud.ec = TALER_JSON_get_error_code2 (data, | ||
162 | data_size); | ||
163 | break; | ||
164 | case MHD_HTTP_TOO_MANY_REQUESTS: | ||
165 | ud.ec = TALER_JSON_get_error_code2 (data, | ||
166 | data_size); | ||
167 | break; | ||
168 | case MHD_HTTP_INTERNAL_SERVER_ERROR: | ||
169 | ud.ec = TALER_JSON_get_error_code2 (data, | ||
170 | data_size); | ||
171 | break; | ||
172 | default: | ||
173 | GNUNET_break (0); | ||
174 | ud.ec = TALER_JSON_get_error_code2 (data, | ||
175 | data_size); | ||
176 | break; | ||
177 | } | ||
178 | tso->cb (tso->cb_cls, | ||
179 | &ud); | ||
180 | tso->cb = NULL; | ||
181 | ANASTASIS_truth_store_cancel (tso); | ||
182 | } | ||
183 | |||
184 | |||
185 | /** | ||
186 | * Handle HTTP header received by curl. | ||
187 | * | ||
188 | * @param buffer one line of HTTP header data | ||
189 | * @param size size of an item | ||
190 | * @param nitems number of items passed | ||
191 | * @param userdata our `struct ANASTASIS_StorePolicyOperation *` | ||
192 | * @return `size * nitems` | ||
193 | */ | ||
194 | static size_t | ||
195 | handle_header (char *buffer, | ||
196 | size_t size, | ||
197 | size_t nitems, | ||
198 | void *userdata) | ||
199 | { | ||
200 | struct ANASTASIS_TruthStoreOperation *tso = userdata; | ||
201 | size_t total = size * nitems; | ||
202 | char *ndup; | ||
203 | const char *hdr_type; | ||
204 | char *hdr_val; | ||
205 | char *sp; | ||
206 | |||
207 | ndup = GNUNET_strndup (buffer, | ||
208 | total); | ||
209 | hdr_type = strtok_r (ndup, | ||
210 | ":", | ||
211 | &sp); | ||
212 | if (NULL == hdr_type) | ||
213 | { | ||
214 | GNUNET_free (ndup); | ||
215 | return total; | ||
216 | } | ||
217 | hdr_val = strtok_r (NULL, | ||
218 | "", | ||
219 | &sp); | ||
220 | if (NULL == hdr_val) | ||
221 | { | ||
222 | GNUNET_free (ndup); | ||
223 | return total; | ||
224 | } | ||
225 | if (' ' == *hdr_val) | ||
226 | hdr_val++; | ||
227 | if (0 == strcasecmp (hdr_type, | ||
228 | ANASTASIS_HTTP_HEADER_TALER)) | ||
229 | { | ||
230 | size_t len; | ||
231 | |||
232 | /* found payment URI we care about! */ | ||
233 | tso->pay_uri = GNUNET_strdup (hdr_val); | ||
234 | len = strlen (tso->pay_uri); | ||
235 | while ( (len > 0) && | ||
236 | ( ('\n' == tso->pay_uri[len - 1]) || | ||
237 | ('\r' == tso->pay_uri[len - 1]) ) ) | ||
238 | { | ||
239 | len--; | ||
240 | tso->pay_uri[len] = '\0'; | ||
241 | } | ||
242 | } | ||
243 | GNUNET_free (ndup); | ||
244 | return total; | ||
245 | } | ||
246 | |||
247 | |||
248 | struct ANASTASIS_TruthStoreOperation * | ||
249 | ANASTASIS_truth_store ( | ||
250 | struct GNUNET_CURL_Context *ctx, | ||
251 | const char *backend_url, | ||
252 | const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, | ||
253 | const char *type, | ||
254 | const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *encrypted_keyshare, | ||
255 | const char *truth_mime, | ||
256 | size_t encrypted_truth_size, | ||
257 | const void *encrypted_truth, | ||
258 | uint32_t payment_years_requested, | ||
259 | struct GNUNET_TIME_Relative payment_timeout, | ||
260 | ANASTASIS_TruthStoreCallback cb, | ||
261 | void *cb_cls) | ||
262 | { | ||
263 | struct ANASTASIS_TruthStoreOperation *tso; | ||
264 | CURL *eh; | ||
265 | char *json_str; | ||
266 | unsigned long long tms; | ||
267 | |||
268 | tms = (unsigned long long) (payment_timeout.rel_value_us | ||
269 | / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); | ||
270 | tso = GNUNET_new (struct ANASTASIS_TruthStoreOperation); | ||
271 | { | ||
272 | char *uuid_str; | ||
273 | char *path; | ||
274 | char timeout_ms[32]; | ||
275 | |||
276 | GNUNET_snprintf (timeout_ms, | ||
277 | sizeof (timeout_ms), | ||
278 | "%llu", | ||
279 | tms); | ||
280 | uuid_str = GNUNET_STRINGS_data_to_string_alloc (uuid, | ||
281 | sizeof (*uuid)); | ||
282 | GNUNET_asprintf (&path, | ||
283 | "truth/%s", | ||
284 | uuid_str); | ||
285 | tso->url = TALER_url_join (backend_url, | ||
286 | path, | ||
287 | "timeout_ms", | ||
288 | (0 != payment_timeout.rel_value_us) | ||
289 | ? timeout_ms | ||
290 | : NULL, | ||
291 | NULL); | ||
292 | GNUNET_free (path); | ||
293 | GNUNET_free (uuid_str); | ||
294 | } | ||
295 | { | ||
296 | json_t *truth_data; | ||
297 | |||
298 | truth_data = json_pack ("{s:o," /* encrypted KeyShare */ | ||
299 | " s:s," /* type */ | ||
300 | " s:o," /* nonce */ | ||
301 | " s:s," /* truth_mime */ | ||
302 | " s:I}", /* payment years */ | ||
303 | "keyshare_data", | ||
304 | GNUNET_JSON_from_data_auto (encrypted_keyshare), | ||
305 | "type", | ||
306 | type, | ||
307 | "encrypted_truth", | ||
308 | GNUNET_JSON_from_data (encrypted_truth, | ||
309 | encrypted_truth_size), | ||
310 | "truth_mime", | ||
311 | (NULL != truth_mime) | ||
312 | ? truth_mime | ||
313 | : "", | ||
314 | "storage_duration_years", | ||
315 | (json_int_t) payment_years_requested); | ||
316 | GNUNET_assert (NULL != truth_data); | ||
317 | json_str = json_dumps (truth_data, | ||
318 | JSON_COMPACT); | ||
319 | GNUNET_assert (NULL != json_str); | ||
320 | json_decref (truth_data); | ||
321 | } | ||
322 | tso->ctx = ctx; | ||
323 | tso->data = json_str; | ||
324 | tso->cb = cb; | ||
325 | tso->cb_cls = cb_cls; | ||
326 | eh = ANASTASIS_curl_easy_get_ (tso->url); | ||
327 | if (0 != tms) | ||
328 | GNUNET_assert (CURLE_OK == | ||
329 | curl_easy_setopt (eh, | ||
330 | CURLOPT_TIMEOUT_MS, | ||
331 | (long) (tms + 5000))); | ||
332 | GNUNET_assert (CURLE_OK == | ||
333 | curl_easy_setopt (eh, | ||
334 | CURLOPT_POSTFIELDS, | ||
335 | json_str)); | ||
336 | GNUNET_assert (CURLE_OK == | ||
337 | curl_easy_setopt (eh, | ||
338 | CURLOPT_POSTFIELDSIZE, | ||
339 | strlen (json_str))); | ||
340 | GNUNET_assert (CURLE_OK == | ||
341 | curl_easy_setopt (eh, | ||
342 | CURLOPT_HEADERFUNCTION, | ||
343 | &handle_header)); | ||
344 | GNUNET_assert (CURLE_OK == | ||
345 | curl_easy_setopt (eh, | ||
346 | CURLOPT_HEADERDATA, | ||
347 | tso)); | ||
348 | tso->job = GNUNET_CURL_job_add_raw (ctx, | ||
349 | eh, | ||
350 | GNUNET_NO, | ||
351 | &handle_truth_store_finished, | ||
352 | tso); | ||
353 | return tso; | ||
354 | } | ||