diff options
Diffstat (limited to 'src/backend/anastasis-httpd_truth_upload.c')
-rw-r--r-- | src/backend/anastasis-httpd_truth_upload.c | 855 |
1 files changed, 855 insertions, 0 deletions
diff --git a/src/backend/anastasis-httpd_truth_upload.c b/src/backend/anastasis-httpd_truth_upload.c new file mode 100644 index 0000000..9767087 --- /dev/null +++ b/src/backend/anastasis-httpd_truth_upload.c | |||
@@ -0,0 +1,855 @@ | |||
1 | /* | ||
2 | This file is part of TALER | ||
3 | Copyright (C) 2019, 2021 Taler Systems SA | ||
4 | |||
5 | TALER is free software; you can redistribute it and/or modify it under the | ||
6 | terms of the GNU Affero 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 Affero General Public License for more details. | ||
12 | |||
13 | You should have received a copy of the GNU Affero General Public License along with | ||
14 | TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> | ||
15 | */ | ||
16 | /** | ||
17 | * @file anastasis-httpd_truth_upload.c | ||
18 | * @brief functions to handle incoming POST request on /truth | ||
19 | * @author Dennis Neufeld | ||
20 | * @author Dominik Meister | ||
21 | * @author Christian Grothoff | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include "anastasis-httpd.h" | ||
25 | #include "anastasis_service.h" | ||
26 | #include "anastasis-httpd_truth.h" | ||
27 | #include <gnunet/gnunet_util_lib.h> | ||
28 | #include <gnunet/gnunet_rest_lib.h> | ||
29 | #include <taler/taler_json_lib.h> | ||
30 | #include <taler/taler_merchant_service.h> | ||
31 | #include <taler/taler_signatures.h> | ||
32 | #include "anastasis_authorization_lib.h" | ||
33 | |||
34 | |||
35 | /** | ||
36 | * Information we track per truth upload. | ||
37 | */ | ||
38 | struct TruthUploadContext | ||
39 | { | ||
40 | |||
41 | /** | ||
42 | * UUID of the truth object we are processing. | ||
43 | */ | ||
44 | struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; | ||
45 | |||
46 | /** | ||
47 | * Kept in DLL for shutdown handling while suspended. | ||
48 | */ | ||
49 | struct TruthUploadContext *next; | ||
50 | |||
51 | /** | ||
52 | * Kept in DLL for shutdown handling while suspended. | ||
53 | */ | ||
54 | struct TruthUploadContext *prev; | ||
55 | |||
56 | /** | ||
57 | * Used while we are awaiting proposal creation. | ||
58 | */ | ||
59 | struct TALER_MERCHANT_PostOrdersHandle *po; | ||
60 | |||
61 | /** | ||
62 | * Used while we are waiting payment. | ||
63 | */ | ||
64 | struct TALER_MERCHANT_OrderMerchantGetHandle *cpo; | ||
65 | |||
66 | /** | ||
67 | * Post parser context. | ||
68 | */ | ||
69 | void *post_ctx; | ||
70 | |||
71 | /** | ||
72 | * Handle to the client request. | ||
73 | */ | ||
74 | struct MHD_Connection *connection; | ||
75 | |||
76 | /** | ||
77 | * Incoming JSON, NULL if not yet available. | ||
78 | */ | ||
79 | json_t *json; | ||
80 | |||
81 | /** | ||
82 | * HTTP response code to use on resume, if non-NULL. | ||
83 | */ | ||
84 | struct MHD_Response *resp; | ||
85 | |||
86 | /** | ||
87 | * When should this request time out? | ||
88 | */ | ||
89 | struct GNUNET_TIME_Absolute timeout; | ||
90 | |||
91 | /** | ||
92 | * Fee that is to be paid for this upload. | ||
93 | */ | ||
94 | struct TALER_Amount upload_fee; | ||
95 | |||
96 | /** | ||
97 | * HTTP response code to use on resume, if resp is set. | ||
98 | */ | ||
99 | unsigned int response_code; | ||
100 | |||
101 | /** | ||
102 | * For how many years must the customer still pay? | ||
103 | */ | ||
104 | unsigned int years_to_pay; | ||
105 | |||
106 | }; | ||
107 | |||
108 | |||
109 | /** | ||
110 | * Head of linked list over all truth upload processes | ||
111 | */ | ||
112 | static struct TruthUploadContext *tuc_head; | ||
113 | |||
114 | /** | ||
115 | * Tail of linked list over all truth upload processes | ||
116 | */ | ||
117 | static struct TruthUploadContext *tuc_tail; | ||
118 | |||
119 | |||
120 | void | ||
121 | AH_truth_upload_shutdown (void) | ||
122 | { | ||
123 | struct TruthUploadContext *tuc; | ||
124 | |||
125 | while (NULL != (tuc = tuc_head)) | ||
126 | { | ||
127 | GNUNET_CONTAINER_DLL_remove (tuc_head, | ||
128 | tuc_tail, | ||
129 | tuc); | ||
130 | if (NULL != tuc->cpo) | ||
131 | { | ||
132 | TALER_MERCHANT_merchant_order_get_cancel (tuc->cpo); | ||
133 | tuc->cpo = NULL; | ||
134 | } | ||
135 | if (NULL != tuc->po) | ||
136 | { | ||
137 | TALER_MERCHANT_orders_post_cancel (tuc->po); | ||
138 | tuc->po = NULL; | ||
139 | } | ||
140 | MHD_resume_connection (tuc->connection); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | |||
145 | /** | ||
146 | * Function called to clean up a `struct TruthUploadContext`. | ||
147 | * | ||
148 | * @param hc general handler context | ||
149 | */ | ||
150 | static void | ||
151 | cleanup_truth_post (struct TM_HandlerContext *hc) | ||
152 | { | ||
153 | struct TruthUploadContext *tuc = hc->ctx; | ||
154 | |||
155 | TALER_MHD_parse_post_cleanup_callback (tuc->post_ctx); | ||
156 | if (NULL != tuc->po) | ||
157 | TALER_MERCHANT_orders_post_cancel (tuc->po); | ||
158 | if (NULL != tuc->cpo) | ||
159 | TALER_MERCHANT_merchant_order_get_cancel (tuc->cpo); | ||
160 | if (NULL != tuc->resp) | ||
161 | MHD_destroy_response (tuc->resp); | ||
162 | if (NULL != tuc->json) | ||
163 | json_decref (tuc->json); | ||
164 | GNUNET_free (tuc); | ||
165 | } | ||
166 | |||
167 | |||
168 | /** | ||
169 | * Transmit a payment request for @a tuc. | ||
170 | * | ||
171 | * @param tuc upload context to generate payment request for | ||
172 | */ | ||
173 | static void | ||
174 | make_payment_request (struct TruthUploadContext *tuc) | ||
175 | { | ||
176 | struct MHD_Response *resp; | ||
177 | |||
178 | /* request payment via Taler */ | ||
179 | resp = MHD_create_response_from_buffer (0, | ||
180 | NULL, | ||
181 | MHD_RESPMEM_PERSISTENT); | ||
182 | GNUNET_assert (NULL != resp); | ||
183 | TALER_MHD_add_global_headers (resp); | ||
184 | { | ||
185 | char *hdr; | ||
186 | const char *pfx; | ||
187 | const char *hn; | ||
188 | |||
189 | if (0 == strncasecmp ("https://", | ||
190 | AH_backend_url, | ||
191 | strlen ("https://"))) | ||
192 | { | ||
193 | pfx = "taler://"; | ||
194 | hn = &AH_backend_url[strlen ("https://")]; | ||
195 | } | ||
196 | else if (0 == strncasecmp ("http://", | ||
197 | AH_backend_url, | ||
198 | strlen ("http://"))) | ||
199 | { | ||
200 | pfx = "taler+http://"; | ||
201 | hn = &AH_backend_url[strlen ("http://")]; | ||
202 | } | ||
203 | else | ||
204 | { | ||
205 | /* This invariant holds as per check in anastasis-httpd.c */ | ||
206 | GNUNET_assert (0); | ||
207 | } | ||
208 | /* This invariant holds as per check in anastasis-httpd.c */ | ||
209 | GNUNET_assert (0 != strlen (hn)); | ||
210 | { | ||
211 | char *order_id; | ||
212 | |||
213 | order_id = GNUNET_STRINGS_data_to_string_alloc ( | ||
214 | &tuc->truth_uuid, | ||
215 | sizeof (tuc->truth_uuid)); | ||
216 | GNUNET_asprintf (&hdr, | ||
217 | "%spay/%s%s/", | ||
218 | pfx, | ||
219 | hn, | ||
220 | order_id); | ||
221 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
222 | "Returning %u %s\n", | ||
223 | MHD_HTTP_PAYMENT_REQUIRED, | ||
224 | order_id); | ||
225 | GNUNET_free (order_id); | ||
226 | } | ||
227 | GNUNET_break (MHD_YES == | ||
228 | MHD_add_response_header (resp, | ||
229 | ANASTASIS_HTTP_HEADER_TALER, | ||
230 | hdr)); | ||
231 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
232 | "TRUTH payment request made: %s\n", | ||
233 | hdr); | ||
234 | GNUNET_free (hdr); | ||
235 | } | ||
236 | tuc->resp = resp; | ||
237 | tuc->response_code = MHD_HTTP_PAYMENT_REQUIRED; | ||
238 | } | ||
239 | |||
240 | |||
241 | /** | ||
242 | * Callbacks of this type are used to serve the result of submitting a | ||
243 | * POST /private/orders request to a merchant. | ||
244 | * | ||
245 | * @param cls our `struct TruthUploadContext` | ||
246 | * @param por response details | ||
247 | */ | ||
248 | static void | ||
249 | proposal_cb (void *cls, | ||
250 | const struct TALER_MERCHANT_PostOrdersReply *por) | ||
251 | { | ||
252 | struct TruthUploadContext *tuc = cls; | ||
253 | |||
254 | tuc->po = NULL; | ||
255 | GNUNET_CONTAINER_DLL_remove (tuc_head, | ||
256 | tuc_tail, | ||
257 | tuc); | ||
258 | MHD_resume_connection (tuc->connection); | ||
259 | AH_trigger_daemon (NULL); | ||
260 | if (MHD_HTTP_OK != por->hr.http_status) | ||
261 | { | ||
262 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
263 | "Backend returned status %u/%d\n", | ||
264 | por->hr.http_status, | ||
265 | (int) por->hr.ec); | ||
266 | GNUNET_break (0); | ||
267 | tuc->resp = TALER_MHD_make_json_pack ( | ||
268 | "{s:I, s:s, s:I, s:I, s:O?}", | ||
269 | "code", | ||
270 | (json_int_t) TALER_EC_ANASTASIS_GENERIC_ORDER_CREATE_BACKEND_ERROR, | ||
271 | "hint", | ||
272 | "Failed to setup order with merchant backend", | ||
273 | "backend-ec", | ||
274 | (json_int_t) por->hr.ec, | ||
275 | "backend-http-status", | ||
276 | (json_int_t) por->hr.http_status, | ||
277 | "backend-reply", | ||
278 | por->hr.reply); | ||
279 | GNUNET_assert (NULL != tuc->resp); | ||
280 | tuc->response_code = MHD_HTTP_BAD_GATEWAY; | ||
281 | return; | ||
282 | } | ||
283 | make_payment_request (tuc); | ||
284 | } | ||
285 | |||
286 | |||
287 | /** | ||
288 | * Callback to process a GET /check-payment request | ||
289 | * | ||
290 | * @param cls our `struct PolicyUploadContext` | ||
291 | * @param hr HTTP response details | ||
292 | * @param osr order status | ||
293 | */ | ||
294 | static void | ||
295 | check_payment_cb (void *cls, | ||
296 | const struct TALER_MERCHANT_HttpResponse *hr, | ||
297 | const struct TALER_MERCHANT_OrderStatusResponse *osr) | ||
298 | { | ||
299 | struct TruthUploadContext *tuc = cls; | ||
300 | |||
301 | tuc->cpo = NULL; | ||
302 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
303 | "Checking backend order status returned %u\n", | ||
304 | hr->http_status); | ||
305 | switch (hr->http_status) | ||
306 | { | ||
307 | case 0: | ||
308 | /* Likely timeout, complain! */ | ||
309 | tuc->response_code = MHD_HTTP_GATEWAY_TIMEOUT; | ||
310 | tuc->resp = TALER_MHD_make_error ( | ||
311 | TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT, | ||
312 | NULL); | ||
313 | break; | ||
314 | case MHD_HTTP_OK: | ||
315 | switch (osr->status) | ||
316 | { | ||
317 | case TALER_MERCHANT_OSC_PAID: | ||
318 | { | ||
319 | enum GNUNET_DB_QueryStatus qs; | ||
320 | unsigned int years; | ||
321 | struct GNUNET_TIME_Relative paid_until; | ||
322 | const json_t *contract; | ||
323 | struct TALER_Amount amount; | ||
324 | struct GNUNET_JSON_Specification cspec[] = { | ||
325 | TALER_JSON_spec_amount ("amount", | ||
326 | AH_currency, | ||
327 | &amount), | ||
328 | GNUNET_JSON_spec_end () | ||
329 | }; | ||
330 | |||
331 | contract = osr->details.paid.contract_terms; | ||
332 | if (GNUNET_OK != | ||
333 | GNUNET_JSON_parse (contract, | ||
334 | cspec, | ||
335 | NULL, NULL)) | ||
336 | { | ||
337 | GNUNET_break (0); | ||
338 | tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; | ||
339 | tuc->resp = TALER_MHD_make_error ( | ||
340 | TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, | ||
341 | "contract terms in database are malformed"); | ||
342 | break; | ||
343 | } | ||
344 | years = TALER_amount_divide2 (&amount, | ||
345 | &AH_truth_upload_fee); | ||
346 | paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, | ||
347 | years); | ||
348 | /* add 1 week grace period, otherwise if a user | ||
349 | wants to pay for 1 year, the first seconds | ||
350 | would have passed between making the payment | ||
351 | and our subsequent check if +1 year was | ||
352 | paid... So we actually say 1 year = 52 weeks | ||
353 | on the server, while the client calculates | ||
354 | with 365 days. */ | ||
355 | paid_until = GNUNET_TIME_relative_add (paid_until, | ||
356 | GNUNET_TIME_UNIT_WEEKS); | ||
357 | qs = db->record_truth_upload_payment ( | ||
358 | db->cls, | ||
359 | &tuc->truth_uuid, | ||
360 | &osr->details.paid.deposit_total, | ||
361 | paid_until); | ||
362 | if (qs <= 0) | ||
363 | { | ||
364 | GNUNET_break (0); | ||
365 | tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; | ||
366 | tuc->resp = TALER_MHD_make_error ( | ||
367 | TALER_EC_GENERIC_DB_STORE_FAILED, | ||
368 | "record_truth_upload_payment"); | ||
369 | break; | ||
370 | } | ||
371 | } | ||
372 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
373 | "Payment confirmed, resuming upload\n"); | ||
374 | break; | ||
375 | case TALER_MERCHANT_OSC_UNPAID: | ||
376 | case TALER_MERCHANT_OSC_CLAIMED: | ||
377 | make_payment_request (tuc); | ||
378 | break; | ||
379 | } | ||
380 | break; | ||
381 | case MHD_HTTP_UNAUTHORIZED: | ||
382 | /* Configuration issue, complain! */ | ||
383 | tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; | ||
384 | tuc->resp = TALER_MHD_make_json_pack ( | ||
385 | "{s:I, s:s, s:I, s:I, s:O?}", | ||
386 | "code", | ||
387 | (json_int_t) TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED, | ||
388 | "hint", | ||
389 | TALER_ErrorCode_get_hint ( | ||
390 | TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED), | ||
391 | "backend-ec", | ||
392 | (json_int_t) hr->ec, | ||
393 | "backend-http-status", | ||
394 | (json_int_t) hr->http_status, | ||
395 | "backend-reply", | ||
396 | hr->reply); | ||
397 | GNUNET_assert (NULL != tuc->resp); | ||
398 | break; | ||
399 | case MHD_HTTP_NOT_FOUND: | ||
400 | /* Setup fresh order */ | ||
401 | { | ||
402 | char *order_id; | ||
403 | json_t *order; | ||
404 | |||
405 | order_id = GNUNET_STRINGS_data_to_string_alloc ( | ||
406 | &tuc->truth_uuid, | ||
407 | sizeof(tuc->truth_uuid)); | ||
408 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
409 | "%u, setting up fresh order %s\n", | ||
410 | MHD_HTTP_NOT_FOUND, | ||
411 | order_id); | ||
412 | order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s}", | ||
413 | "amount", | ||
414 | TALER_JSON_from_amount (&tuc->upload_fee), | ||
415 | "summary", | ||
416 | "Anastasis challenge storage fee", | ||
417 | "products", | ||
418 | "description", "challenge storage fee", | ||
419 | "quantity", (json_int_t) tuc->years_to_pay, | ||
420 | "unit", "years", | ||
421 | |||
422 | "order_id", | ||
423 | order_id); | ||
424 | GNUNET_free (order_id); | ||
425 | tuc->po = TALER_MERCHANT_orders_post2 (AH_ctx, | ||
426 | AH_backend_url, | ||
427 | order, | ||
428 | GNUNET_TIME_UNIT_ZERO, | ||
429 | NULL, /* no payment target */ | ||
430 | 0, | ||
431 | NULL, /* no inventory products */ | ||
432 | 0, | ||
433 | NULL, /* no uuids */ | ||
434 | false, /* do NOT require claim token */ | ||
435 | &proposal_cb, | ||
436 | tuc); | ||
437 | AH_trigger_curl (); | ||
438 | json_decref (order); | ||
439 | return; | ||
440 | } | ||
441 | default: | ||
442 | /* Unexpected backend response */ | ||
443 | tuc->response_code = MHD_HTTP_BAD_GATEWAY; | ||
444 | tuc->resp = TALER_MHD_make_json_pack ( | ||
445 | "{s:I, s:s, s:I, s:I, s:O?}", | ||
446 | "code", | ||
447 | (json_int_t) TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR, | ||
448 | "hint", | ||
449 | TALER_ErrorCode_get_hint (TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR), | ||
450 | "backend-ec", | ||
451 | (json_int_t) hr->ec, | ||
452 | "backend-http-status", | ||
453 | (json_int_t) hr->http_status, | ||
454 | "backend-reply", | ||
455 | hr->reply); | ||
456 | break; | ||
457 | } | ||
458 | GNUNET_CONTAINER_DLL_remove (tuc_head, | ||
459 | tuc_tail, | ||
460 | tuc); | ||
461 | MHD_resume_connection (tuc->connection); | ||
462 | AH_trigger_daemon (NULL); | ||
463 | } | ||
464 | |||
465 | |||
466 | /** | ||
467 | * Helper function used to ask our backend to begin processing a | ||
468 | * payment for the truth upload. May perform asynchronous operations | ||
469 | * by suspending the connection if required. | ||
470 | * | ||
471 | * @param tuc context to begin payment for. | ||
472 | * @return MHD status code | ||
473 | */ | ||
474 | static MHD_RESULT | ||
475 | begin_payment (struct TruthUploadContext *tuc) | ||
476 | { | ||
477 | char *order_id; | ||
478 | struct GNUNET_TIME_Relative timeout; | ||
479 | |||
480 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
481 | "Checking backend order status...\n"); | ||
482 | timeout = GNUNET_TIME_absolute_get_remaining (tuc->timeout); | ||
483 | order_id = GNUNET_STRINGS_data_to_string_alloc ( | ||
484 | &tuc->truth_uuid, | ||
485 | sizeof (tuc->truth_uuid)); | ||
486 | tuc->cpo = TALER_MERCHANT_merchant_order_get (AH_ctx, | ||
487 | AH_backend_url, | ||
488 | order_id, | ||
489 | NULL /* our payments are NOT session-bound */, | ||
490 | false, | ||
491 | timeout, | ||
492 | &check_payment_cb, | ||
493 | tuc); | ||
494 | GNUNET_free (order_id); | ||
495 | if (NULL == tuc->cpo) | ||
496 | { | ||
497 | GNUNET_break (0); | ||
498 | return TALER_MHD_reply_with_error (tuc->connection, | ||
499 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
500 | TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED, | ||
501 | "Could not check order status"); | ||
502 | } | ||
503 | GNUNET_CONTAINER_DLL_insert (tuc_head, | ||
504 | tuc_tail, | ||
505 | tuc); | ||
506 | MHD_suspend_connection (tuc->connection); | ||
507 | return MHD_YES; | ||
508 | } | ||
509 | |||
510 | |||
511 | int | ||
512 | AH_handler_truth_post ( | ||
513 | struct MHD_Connection *connection, | ||
514 | struct TM_HandlerContext *hc, | ||
515 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
516 | const char *truth_data, | ||
517 | size_t *truth_data_size) | ||
518 | { | ||
519 | struct TruthUploadContext *tuc = hc->ctx; | ||
520 | MHD_RESULT ret; | ||
521 | int res; | ||
522 | struct ANASTASIS_CRYPTO_EncryptedKeyShareP keyshare_data; | ||
523 | void *encrypted_truth; | ||
524 | size_t encrypted_truth_size; | ||
525 | const char *truth_mime; | ||
526 | const char *type; | ||
527 | enum GNUNET_DB_QueryStatus qs; | ||
528 | uint32_t storage_years; | ||
529 | struct GNUNET_TIME_Absolute paid_until; | ||
530 | struct GNUNET_JSON_Specification spec[] = { | ||
531 | GNUNET_JSON_spec_fixed_auto ("keyshare_data", | ||
532 | &keyshare_data), | ||
533 | GNUNET_JSON_spec_string ("type", | ||
534 | &type), | ||
535 | GNUNET_JSON_spec_varsize ("encrypted_truth", | ||
536 | &encrypted_truth, | ||
537 | &encrypted_truth_size), | ||
538 | GNUNET_JSON_spec_string ("truth_mime", | ||
539 | &truth_mime), | ||
540 | GNUNET_JSON_spec_uint32 ("storage_duration_years", | ||
541 | &storage_years), | ||
542 | GNUNET_JSON_spec_end () | ||
543 | }; | ||
544 | |||
545 | if (NULL == tuc) | ||
546 | { | ||
547 | tuc = GNUNET_new (struct TruthUploadContext); | ||
548 | tuc->connection = connection; | ||
549 | tuc->truth_uuid = *truth_uuid; | ||
550 | hc->ctx = tuc; | ||
551 | hc->cc = &cleanup_truth_post; | ||
552 | |||
553 | /* check for excessive upload */ | ||
554 | { | ||
555 | const char *lens; | ||
556 | unsigned long len; | ||
557 | char dummy; | ||
558 | |||
559 | lens = MHD_lookup_connection_value (connection, | ||
560 | MHD_HEADER_KIND, | ||
561 | MHD_HTTP_HEADER_CONTENT_LENGTH); | ||
562 | if ( (NULL == lens) || | ||
563 | (1 != sscanf (lens, | ||
564 | "%lu%c", | ||
565 | &len, | ||
566 | &dummy)) ) | ||
567 | { | ||
568 | GNUNET_break_op (0); | ||
569 | return TALER_MHD_reply_with_error ( | ||
570 | connection, | ||
571 | MHD_HTTP_BAD_REQUEST, | ||
572 | (NULL == lens) | ||
573 | ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH | ||
574 | : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH, | ||
575 | NULL); | ||
576 | } | ||
577 | if (len / 1024 / 1024 >= AH_upload_limit_mb) | ||
578 | { | ||
579 | GNUNET_break_op (0); | ||
580 | return TALER_MHD_reply_with_error (connection, | ||
581 | MHD_HTTP_PAYLOAD_TOO_LARGE, | ||
582 | TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH, | ||
583 | "Content-length value not acceptable"); | ||
584 | } | ||
585 | } | ||
586 | |||
587 | { | ||
588 | const char *long_poll_timeout_ms; | ||
589 | |||
590 | long_poll_timeout_ms = MHD_lookup_connection_value (connection, | ||
591 | MHD_GET_ARGUMENT_KIND, | ||
592 | "timeout_ms"); | ||
593 | if (NULL != long_poll_timeout_ms) | ||
594 | { | ||
595 | unsigned int timeout; | ||
596 | |||
597 | if (1 != sscanf (long_poll_timeout_ms, | ||
598 | "%u", | ||
599 | &timeout)) | ||
600 | { | ||
601 | GNUNET_break_op (0); | ||
602 | return TALER_MHD_reply_with_error (connection, | ||
603 | MHD_HTTP_BAD_REQUEST, | ||
604 | TALER_EC_GENERIC_PARAMETER_MALFORMED, | ||
605 | "timeout_ms (must be non-negative number)"); | ||
606 | } | ||
607 | tuc->timeout | ||
608 | = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply ( | ||
609 | GNUNET_TIME_UNIT_MILLISECONDS, | ||
610 | timeout)); | ||
611 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
612 | "Long polling for %u ms enabled\n", | ||
613 | timeout); | ||
614 | } | ||
615 | else | ||
616 | { | ||
617 | tuc->timeout = GNUNET_TIME_relative_to_absolute ( | ||
618 | GNUNET_TIME_UNIT_SECONDS); | ||
619 | } | ||
620 | } | ||
621 | |||
622 | } /* end 'if (NULL == tuc)' */ | ||
623 | |||
624 | if (NULL != tuc->resp) | ||
625 | { | ||
626 | /* We generated a response asynchronously, queue that */ | ||
627 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
628 | "Returning asynchronously generated response with HTTP status %u\n", | ||
629 | tuc->response_code); | ||
630 | ret = MHD_queue_response (connection, | ||
631 | tuc->response_code, | ||
632 | tuc->resp); | ||
633 | GNUNET_break (MHD_YES == ret); | ||
634 | MHD_destroy_response (tuc->resp); | ||
635 | tuc->resp = NULL; | ||
636 | return ret; | ||
637 | } | ||
638 | |||
639 | if (NULL == tuc->json) | ||
640 | { | ||
641 | res = TALER_MHD_parse_post_json (connection, | ||
642 | &tuc->post_ctx, | ||
643 | truth_data, | ||
644 | truth_data_size, | ||
645 | &tuc->json); | ||
646 | if (GNUNET_SYSERR == res) | ||
647 | { | ||
648 | GNUNET_break (0); | ||
649 | return MHD_NO; | ||
650 | } | ||
651 | if ( (GNUNET_NO == res) || | ||
652 | (NULL == tuc->json) ) | ||
653 | return MHD_YES; | ||
654 | } | ||
655 | res = TALER_MHD_parse_json_data (connection, | ||
656 | tuc->json, | ||
657 | spec); | ||
658 | if (GNUNET_SYSERR == res) | ||
659 | { | ||
660 | GNUNET_break (0); | ||
661 | return MHD_NO; /* hard failure */ | ||
662 | } | ||
663 | if (GNUNET_NO == res) | ||
664 | { | ||
665 | GNUNET_break_op (0); | ||
666 | return MHD_YES; /* failure */ | ||
667 | } | ||
668 | |||
669 | /* check method is supported */ | ||
670 | { | ||
671 | struct TALER_Amount dummy; | ||
672 | |||
673 | if ( (0 != strcmp ("question", | ||
674 | type)) && | ||
675 | (NULL == | ||
676 | ANASTASIS_authorization_plugin_load (type, | ||
677 | AH_cfg, | ||
678 | &dummy)) ) | ||
679 | { | ||
680 | GNUNET_JSON_parse_free (spec); | ||
681 | return TALER_MHD_reply_with_error (connection, | ||
682 | MHD_HTTP_BAD_REQUEST, | ||
683 | TALER_EC_ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED, | ||
684 | type); | ||
685 | } | ||
686 | } | ||
687 | |||
688 | if (storage_years > ANASTASIS_MAX_YEARS_STORAGE) | ||
689 | { | ||
690 | GNUNET_break_op (0); | ||
691 | return TALER_MHD_reply_with_error (connection, | ||
692 | MHD_HTTP_BAD_REQUEST, | ||
693 | TALER_EC_GENERIC_PARAMETER_MALFORMED, | ||
694 | "storage_duration_years"); | ||
695 | } | ||
696 | if (0 == storage_years) | ||
697 | storage_years = 1; | ||
698 | |||
699 | { | ||
700 | struct TALER_Amount zero_amount; | ||
701 | |||
702 | TALER_amount_set_zero (AH_currency, | ||
703 | &zero_amount); | ||
704 | if (0 != TALER_amount_cmp (&AH_truth_upload_fee, | ||
705 | &zero_amount)) | ||
706 | { | ||
707 | struct GNUNET_TIME_Absolute desired_until; | ||
708 | enum GNUNET_DB_QueryStatus qs; | ||
709 | |||
710 | desired_until | ||
711 | = GNUNET_TIME_relative_to_absolute ( | ||
712 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, | ||
713 | storage_years)); | ||
714 | qs = db->check_truth_upload_paid (db->cls, | ||
715 | truth_uuid, | ||
716 | &paid_until); | ||
717 | if (qs < 0) | ||
718 | return TALER_MHD_reply_with_error (connection, | ||
719 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
720 | TALER_EC_GENERIC_DB_FETCH_FAILED, | ||
721 | NULL); | ||
722 | if ( (0 == qs) || | ||
723 | (paid_until.abs_value_us < desired_until.abs_value_us) ) | ||
724 | { | ||
725 | struct GNUNET_TIME_Absolute now; | ||
726 | struct GNUNET_TIME_Relative rem; | ||
727 | |||
728 | now = GNUNET_TIME_absolute_get (); | ||
729 | if (paid_until.abs_value_us < now.abs_value_us) | ||
730 | paid_until = now; | ||
731 | rem = GNUNET_TIME_absolute_get_difference (paid_until, | ||
732 | desired_until); | ||
733 | tuc->years_to_pay = rem.rel_value_us | ||
734 | / GNUNET_TIME_UNIT_YEARS.rel_value_us; | ||
735 | if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) | ||
736 | tuc->years_to_pay++; | ||
737 | if (0 > | ||
738 | TALER_amount_multiply (&tuc->upload_fee, | ||
739 | &AH_truth_upload_fee, | ||
740 | tuc->years_to_pay)) | ||
741 | { | ||
742 | GNUNET_break_op (0); | ||
743 | return TALER_MHD_reply_with_error (connection, | ||
744 | MHD_HTTP_BAD_REQUEST, | ||
745 | TALER_EC_GENERIC_PARAMETER_MALFORMED, | ||
746 | "storage_duration_years"); | ||
747 | } | ||
748 | if ( (0 != tuc->upload_fee.fraction) || | ||
749 | (0 != tuc->upload_fee.value) ) | ||
750 | { | ||
751 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
752 | "Truth upload payment required (%d)!\n", | ||
753 | qs); | ||
754 | return begin_payment (tuc); | ||
755 | } | ||
756 | } | ||
757 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
758 | "TRUTH paid until %s (%d)!\n", | ||
759 | GNUNET_STRINGS_relative_time_to_string ( | ||
760 | GNUNET_TIME_absolute_get_remaining ( | ||
761 | paid_until), | ||
762 | GNUNET_YES), | ||
763 | qs); | ||
764 | } | ||
765 | else | ||
766 | { | ||
767 | paid_until | ||
768 | = GNUNET_TIME_relative_to_absolute ( | ||
769 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, | ||
770 | ANASTASIS_MAX_YEARS_STORAGE)); | ||
771 | } | ||
772 | } | ||
773 | |||
774 | |||
775 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
776 | "Storing truth until %s!\n", | ||
777 | GNUNET_STRINGS_absolute_time_to_string (paid_until)); | ||
778 | qs = db->store_truth (db->cls, | ||
779 | truth_uuid, | ||
780 | &keyshare_data, | ||
781 | truth_mime, | ||
782 | encrypted_truth, | ||
783 | encrypted_truth_size, | ||
784 | type, | ||
785 | GNUNET_TIME_absolute_get_remaining (paid_until)); | ||
786 | switch (qs) | ||
787 | { | ||
788 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
789 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
790 | GNUNET_break (0); | ||
791 | GNUNET_JSON_parse_free (spec); | ||
792 | return TALER_MHD_reply_with_error (connection, | ||
793 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
794 | TALER_EC_GENERIC_DB_INVARIANT_FAILURE, | ||
795 | "store_truth"); | ||
796 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
797 | { | ||
798 | void *xtruth; | ||
799 | size_t xtruth_size; | ||
800 | char *xtruth_mime; | ||
801 | char *xmethod; | ||
802 | bool ok = false; | ||
803 | |||
804 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == | ||
805 | db->get_escrow_challenge (db->cls, | ||
806 | truth_uuid, | ||
807 | &xtruth, | ||
808 | &xtruth_size, | ||
809 | &xtruth_mime, | ||
810 | &xmethod)) | ||
811 | { | ||
812 | ok = ( (xtruth_size == encrypted_truth_size) && | ||
813 | (0 == strcmp (xmethod, | ||
814 | type)) && | ||
815 | (0 == strcmp (truth_mime, | ||
816 | xtruth_mime)) && | ||
817 | (0 == memcmp (xtruth, | ||
818 | encrypted_truth, | ||
819 | xtruth_size)) ); | ||
820 | GNUNET_free (encrypted_truth); | ||
821 | GNUNET_free (xtruth_mime); | ||
822 | GNUNET_free (xmethod); | ||
823 | } | ||
824 | if (! ok) | ||
825 | { | ||
826 | GNUNET_JSON_parse_free (spec); | ||
827 | |||
828 | return TALER_MHD_reply_with_error (connection, | ||
829 | MHD_HTTP_CONFLICT, | ||
830 | TALER_EC_ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS, | ||
831 | NULL); | ||
832 | } | ||
833 | /* idempotency detected, intentional fall through! */ | ||
834 | } | ||
835 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
836 | { | ||
837 | struct MHD_Response *resp; | ||
838 | |||
839 | GNUNET_JSON_parse_free (spec); | ||
840 | resp = MHD_create_response_from_buffer (0, | ||
841 | NULL, | ||
842 | MHD_RESPMEM_PERSISTENT); | ||
843 | TALER_MHD_add_global_headers (resp); | ||
844 | ret = MHD_queue_response (connection, | ||
845 | MHD_HTTP_NO_CONTENT, | ||
846 | resp); | ||
847 | MHD_destroy_response (resp); | ||
848 | GNUNET_break (MHD_YES == ret); | ||
849 | return ret; | ||
850 | } | ||
851 | } | ||
852 | GNUNET_JSON_parse_free (spec); | ||
853 | GNUNET_break (0); | ||
854 | return MHD_NO; | ||
855 | } | ||