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