aboutsummaryrefslogtreecommitdiff
path: root/src/restclient/anastasis_api_truth_store.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/restclient/anastasis_api_truth_store.c')
-rw-r--r--src/restclient/anastasis_api_truth_store.c354
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
33struct 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
72void
73ANASTASIS_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 */
96static void
97handle_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 */
194static size_t
195handle_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
248struct ANASTASIS_TruthStoreOperation *
249ANASTASIS_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}