aboutsummaryrefslogtreecommitdiff
path: root/src/backend/anastasis-httpd_truth_upload.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/anastasis-httpd_truth_upload.c')
-rw-r--r--src/backend/anastasis-httpd_truth_upload.c855
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 */
38struct 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 */
112static struct TruthUploadContext *tuc_head;
113
114/**
115 * Tail of linked list over all truth upload processes
116 */
117static struct TruthUploadContext *tuc_tail;
118
119
120void
121AH_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 */
150static void
151cleanup_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 */
173static void
174make_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 */
248static void
249proposal_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 */
294static void
295check_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 */
474static MHD_RESULT
475begin_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
511int
512AH_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}