anastasis-httpd_truth-upload.c (26384B)
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 = ANASTASIS_DB_record_truth_upload_payment ( 361 &tuc->truth_uuid, 362 &osr->details.ok.details.paid.deposit_total, 363 paid_until); 364 if (qs <= 0) 365 { 366 GNUNET_break (0); 367 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 368 tuc->resp = TALER_MHD_make_error ( 369 TALER_EC_GENERIC_DB_STORE_FAILED, 370 "record_truth_upload_payment"); 371 break; 372 } 373 } 374 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 375 "Payment confirmed, resuming upload\n"); 376 break; 377 case TALER_MERCHANT_OSC_UNPAID: 378 case TALER_MERCHANT_OSC_CLAIMED: 379 make_payment_request (tuc); 380 break; 381 } 382 break; 383 case MHD_HTTP_UNAUTHORIZED: 384 /* Configuration issue, complain! */ 385 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 386 tuc->resp = TALER_MHD_MAKE_JSON_PACK ( 387 GNUNET_JSON_pack_uint64 ("code", 388 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED), 389 GNUNET_JSON_pack_string ("hint", 390 TALER_ErrorCode_get_hint ( 391 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED)), 392 GNUNET_JSON_pack_uint64 ("backend-ec", 393 hr->ec), 394 GNUNET_JSON_pack_uint64 ("backend-http-status", 395 hr->http_status), 396 GNUNET_JSON_pack_allow_null ( 397 GNUNET_JSON_pack_object_incref ("backend-reply", 398 (json_t *) hr->reply))); 399 GNUNET_assert (NULL != tuc->resp); 400 break; 401 case MHD_HTTP_NOT_FOUND: 402 /* Setup fresh order */ 403 { 404 char *order_id; 405 json_t *order; 406 407 order_id = GNUNET_STRINGS_data_to_string_alloc ( 408 &tuc->truth_uuid, 409 sizeof(tuc->truth_uuid)); 410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 411 "%u, setting up fresh order %s\n", 412 MHD_HTTP_NOT_FOUND, 413 order_id); 414 order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s}", 415 "amount", 416 TALER_JSON_from_amount (&tuc->upload_fee), 417 "summary", 418 "Anastasis challenge storage fee", 419 "products", 420 "description", "challenge storage fee", 421 "quantity", (json_int_t) tuc->years_to_pay, 422 "unit", "years", 423 "order_id", 424 order_id); 425 GNUNET_free (order_id); 426 tuc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx, 427 AH_backend_url, 428 order); 429 GNUNET_assert (NULL != tuc->po); 430 GNUNET_assert ( 431 GNUNET_OK == 432 TALER_MERCHANT_post_private_orders_set_options ( 433 tuc->po, 434 TALER_MERCHANT_post_private_orders_option_create_token (false))); 435 GNUNET_assert (TALER_EC_NONE == 436 TALER_MERCHANT_post_private_orders_start (tuc->po, 437 &proposal_cb, 438 tuc)); 439 AH_trigger_curl (); 440 json_decref (order); 441 return; 442 } 443 default: 444 /* Unexpected backend response */ 445 tuc->response_code = MHD_HTTP_BAD_GATEWAY; 446 tuc->resp = TALER_MHD_MAKE_JSON_PACK ( 447 GNUNET_JSON_pack_uint64 ("code", 448 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR), 449 GNUNET_JSON_pack_string ("hint", 450 TALER_ErrorCode_get_hint ( 451 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR)), 452 GNUNET_JSON_pack_uint64 ("backend-ec", 453 (json_int_t) hr->ec), 454 GNUNET_JSON_pack_uint64 ("backend-http-status", 455 (json_int_t) hr->http_status), 456 GNUNET_JSON_pack_allow_null ( 457 GNUNET_JSON_pack_object_incref ("backend-reply", 458 (json_t *) hr->reply))); 459 break; 460 } 461 GNUNET_CONTAINER_DLL_remove (tuc_head, 462 tuc_tail, 463 tuc); 464 MHD_resume_connection (tuc->connection); 465 AH_trigger_daemon (NULL); 466 } 467 468 469 /** 470 * Helper function used to ask our backend to begin processing a 471 * payment for the truth upload. May perform asynchronous operations 472 * by suspending the connection if required. 473 * 474 * @param tuc context to begin payment for. 475 * @return MHD status code 476 */ 477 static enum MHD_Result 478 begin_payment (struct TruthUploadContext *tuc) 479 { 480 char *order_id; 481 struct GNUNET_TIME_Relative timeout; 482 483 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 484 "Checking backend order status...\n"); 485 timeout = GNUNET_TIME_absolute_get_remaining (tuc->timeout); 486 order_id = GNUNET_STRINGS_data_to_string_alloc ( 487 &tuc->truth_uuid, 488 sizeof (tuc->truth_uuid)); 489 tuc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx, 490 AH_backend_url, 491 order_id); 492 GNUNET_free (order_id); 493 if (NULL == tuc->cpo) 494 { 495 GNUNET_break (0); 496 return TALER_MHD_reply_with_error (tuc->connection, 497 MHD_HTTP_INTERNAL_SERVER_ERROR, 498 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED, 499 "Could not check order status"); 500 } 501 GNUNET_assert (GNUNET_OK == 502 TALER_MERCHANT_get_private_order_set_options ( 503 tuc->cpo, 504 TALER_MERCHANT_get_private_order_option_timeout (timeout))); 505 if (TALER_EC_NONE != 506 TALER_MERCHANT_get_private_order_start (tuc->cpo, 507 &check_payment_cb, 508 tuc)) 509 { 510 GNUNET_break (0); 511 return TALER_MHD_reply_with_error (tuc->connection, 512 MHD_HTTP_INTERNAL_SERVER_ERROR, 513 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED, 514 "Could not check order status"); 515 } 516 GNUNET_CONTAINER_DLL_insert (tuc_head, 517 tuc_tail, 518 tuc); 519 MHD_suspend_connection (tuc->connection); 520 return MHD_YES; 521 } 522 523 524 enum MHD_Result 525 AH_handler_truth_post ( 526 struct MHD_Connection *connection, 527 struct TM_HandlerContext *hc, 528 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 529 const char *truth_data, 530 size_t *truth_data_size) 531 { 532 struct TruthUploadContext *tuc = hc->ctx; 533 enum MHD_Result ret; 534 int res; 535 struct ANASTASIS_CRYPTO_EncryptedKeyShareP key_share_data; 536 void *encrypted_truth; 537 size_t encrypted_truth_size; 538 const char *truth_mime = NULL; 539 const char *type; 540 uint32_t storage_years; 541 struct GNUNET_TIME_Timestamp paid_until 542 = GNUNET_TIME_UNIT_ZERO_TS; 543 struct GNUNET_JSON_Specification spec[] = { 544 GNUNET_JSON_spec_fixed_auto ("key_share_data", 545 &key_share_data), 546 GNUNET_JSON_spec_string ("type", 547 &type), 548 GNUNET_JSON_spec_varsize ("encrypted_truth", 549 &encrypted_truth, 550 &encrypted_truth_size), 551 GNUNET_JSON_spec_mark_optional ( 552 GNUNET_JSON_spec_string ("truth_mime", 553 &truth_mime), 554 NULL), 555 GNUNET_JSON_spec_uint32 ("storage_duration_years", 556 &storage_years), 557 GNUNET_JSON_spec_end () 558 }; 559 560 if (NULL == tuc) 561 { 562 tuc = GNUNET_new (struct TruthUploadContext); 563 tuc->connection = connection; 564 tuc->truth_uuid = *truth_uuid; 565 hc->ctx = tuc; 566 hc->cc = &cleanup_truth_post; 567 TALER_MHD_check_content_length (connection, 568 AH_upload_limit_mb * 1024LLU * 1024LLU); 569 tuc->timeout = GNUNET_TIME_relative_to_absolute ( 570 GNUNET_TIME_UNIT_SECONDS); 571 TALER_MHD_parse_request_timeout (connection, 572 &tuc->timeout); 573 } /* end 'if (NULL == tuc)' */ 574 575 if (NULL != tuc->resp) 576 { 577 /* We generated a response asynchronously, queue that */ 578 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 579 "Returning asynchronously generated response with HTTP status %u\n", 580 tuc->response_code); 581 ret = MHD_queue_response (connection, 582 tuc->response_code, 583 tuc->resp); 584 GNUNET_break (MHD_YES == ret); 585 MHD_destroy_response (tuc->resp); 586 tuc->resp = NULL; 587 return ret; 588 } 589 590 if (NULL == tuc->json) 591 { 592 res = TALER_MHD_parse_post_json (connection, 593 &tuc->post_ctx, 594 truth_data, 595 truth_data_size, 596 &tuc->json); 597 if (GNUNET_SYSERR == res) 598 { 599 GNUNET_break (0); 600 return MHD_NO; 601 } 602 if ( (GNUNET_NO == res) || 603 (NULL == tuc->json) ) 604 return MHD_YES; 605 } 606 res = TALER_MHD_parse_json_data (connection, 607 tuc->json, 608 spec); 609 if (GNUNET_SYSERR == res) 610 { 611 GNUNET_break (0); 612 return MHD_NO; /* hard failure */ 613 } 614 if (GNUNET_NO == res) 615 { 616 GNUNET_break_op (0); 617 return MHD_YES; /* failure */ 618 } 619 620 /* check method is supported */ 621 if ( (0 != strcmp ("question", 622 type)) && 623 (NULL == 624 ANASTASIS_authorization_plugin_load (type, 625 AH_cfg)) ) 626 { 627 GNUNET_JSON_parse_free (spec); 628 return TALER_MHD_reply_with_error (connection, 629 MHD_HTTP_BAD_REQUEST, 630 TALER_EC_ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED, 631 type); 632 } 633 634 if (storage_years > ANASTASIS_MAX_YEARS_STORAGE) 635 { 636 GNUNET_break_op (0); 637 return TALER_MHD_reply_with_error (connection, 638 MHD_HTTP_BAD_REQUEST, 639 TALER_EC_GENERIC_PARAMETER_MALFORMED, 640 "storage_duration_years"); 641 } 642 if (0 == storage_years) 643 storage_years = 1; 644 645 if (! TALER_amount_is_zero (&AH_truth_upload_fee)) 646 { 647 struct GNUNET_TIME_Timestamp desired_until; 648 enum GNUNET_DB_QueryStatus qs; 649 650 desired_until 651 = GNUNET_TIME_relative_to_timestamp ( 652 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 653 storage_years)); 654 qs = ANASTASIS_DB_check_truth_upload_paid ( 655 truth_uuid, 656 &paid_until); 657 if (qs < 0) 658 return TALER_MHD_reply_with_error (connection, 659 MHD_HTTP_INTERNAL_SERVER_ERROR, 660 TALER_EC_GENERIC_DB_FETCH_FAILED, 661 NULL); 662 if ( (0 == qs) || 663 (GNUNET_TIME_timestamp_cmp (paid_until, 664 <, 665 desired_until) ) ) 666 { 667 struct GNUNET_TIME_Relative rem; 668 669 if (GNUNET_TIME_absolute_is_past (paid_until.abs_time)) 670 paid_until = GNUNET_TIME_timestamp_get (); 671 rem = GNUNET_TIME_absolute_get_difference (paid_until.abs_time, 672 desired_until.abs_time); 673 tuc->years_to_pay = rem.rel_value_us 674 / GNUNET_TIME_UNIT_YEARS.rel_value_us; 675 if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) 676 tuc->years_to_pay++; 677 if (0 > 678 TALER_amount_multiply (&tuc->upload_fee, 679 &AH_truth_upload_fee, 680 tuc->years_to_pay)) 681 { 682 GNUNET_break_op (0); 683 return TALER_MHD_reply_with_error (connection, 684 MHD_HTTP_BAD_REQUEST, 685 TALER_EC_GENERIC_PARAMETER_MALFORMED, 686 "storage_duration_years"); 687 } 688 if (! TALER_amount_is_zero (&tuc->upload_fee)) 689 { 690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 691 "Truth upload payment required (%d)!\n", 692 qs); 693 return begin_payment (tuc); 694 } 695 } 696 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 697 "TRUTH paid until %s (%d)!\n", 698 GNUNET_TIME_relative2s ( 699 GNUNET_TIME_absolute_get_remaining ( 700 paid_until.abs_time), 701 GNUNET_YES), 702 qs); 703 } 704 else 705 { 706 paid_until 707 = GNUNET_TIME_relative_to_timestamp ( 708 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 709 ANASTASIS_MAX_YEARS_STORAGE)); 710 } 711 712 713 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 714 "Storing truth %s until %s!\n", 715 TALER_B2S (truth_uuid), 716 GNUNET_TIME_timestamp2s (paid_until)); 717 { 718 enum GNUNET_DB_QueryStatus qs; 719 720 qs = ANASTASIS_DB_store_truth ( 721 truth_uuid, 722 &key_share_data, 723 (NULL == truth_mime) 724 ? "" 725 : truth_mime, 726 encrypted_truth, 727 encrypted_truth_size, 728 type, 729 GNUNET_TIME_absolute_get_remaining ( 730 paid_until.abs_time)); 731 switch (qs) 732 { 733 case GNUNET_DB_STATUS_HARD_ERROR: 734 case GNUNET_DB_STATUS_SOFT_ERROR: 735 GNUNET_break (0); 736 GNUNET_JSON_parse_free (spec); 737 return TALER_MHD_reply_with_error (connection, 738 MHD_HTTP_INTERNAL_SERVER_ERROR, 739 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 740 "store_truth"); 741 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 742 { 743 void *xtruth; 744 size_t xtruth_size; 745 char *xtruth_mime; 746 char *xmethod; 747 bool ok = false; 748 749 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 750 ANASTASIS_DB_get_escrow_challenge ( 751 truth_uuid, 752 &xtruth, 753 &xtruth_size, 754 &xtruth_mime, 755 &xmethod)) 756 { 757 ok = ( (xtruth_size == encrypted_truth_size) && 758 (0 == strcmp (xmethod, 759 type)) && 760 (0 == strcmp (((NULL == truth_mime) ? "" : truth_mime), 761 ((NULL == xtruth_mime) ? "" : xtruth_mime))) && 762 (0 == memcmp (xtruth, 763 encrypted_truth, 764 xtruth_size)) ); 765 GNUNET_free (encrypted_truth); 766 GNUNET_free (xtruth_mime); 767 GNUNET_free (xmethod); 768 } 769 if (! ok) 770 { 771 GNUNET_JSON_parse_free (spec); 772 773 return TALER_MHD_reply_with_error (connection, 774 MHD_HTTP_CONFLICT, 775 TALER_EC_ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS, 776 NULL); 777 } 778 /* idempotency detected, intentional fall through! */ 779 } 780 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 781 { 782 struct MHD_Response *resp; 783 784 GNUNET_JSON_parse_free (spec); 785 resp = MHD_create_response_from_buffer (0, 786 NULL, 787 MHD_RESPMEM_PERSISTENT); 788 TALER_MHD_add_global_headers (resp, 789 false); 790 ret = MHD_queue_response (connection, 791 MHD_HTTP_NO_CONTENT, 792 resp); 793 MHD_destroy_response (resp); 794 GNUNET_break (MHD_YES == ret); 795 return ret; 796 } 797 } 798 } 799 GNUNET_JSON_parse_free (spec); 800 GNUNET_break (0); 801 return MHD_NO; 802 }