anastasis-httpd_truth-upload.c (26135B)
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 #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 false); 185 { 186 char *hdr; 187 const char *pfx; 188 const char *hn; 189 190 if (0 == strncasecmp ("https://", 191 AH_backend_url, 192 strlen ("https://"))) 193 { 194 pfx = "taler://"; 195 hn = &AH_backend_url[strlen ("https://")]; 196 } 197 else if (0 == strncasecmp ("http://", 198 AH_backend_url, 199 strlen ("http://"))) 200 { 201 pfx = "taler+http://"; 202 hn = &AH_backend_url[strlen ("http://")]; 203 } 204 else 205 { 206 /* This invariant holds as per check in anastasis-httpd.c */ 207 GNUNET_assert (0); 208 } 209 /* This invariant holds as per check in anastasis-httpd.c */ 210 GNUNET_assert (0 != strlen (hn)); 211 { 212 char *order_id; 213 214 order_id = GNUNET_STRINGS_data_to_string_alloc ( 215 &tuc->truth_uuid, 216 sizeof (tuc->truth_uuid)); 217 GNUNET_asprintf (&hdr, 218 "%spay/%s%s/", 219 pfx, 220 hn, 221 order_id); 222 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 223 "Returning %u %s\n", 224 MHD_HTTP_PAYMENT_REQUIRED, 225 order_id); 226 GNUNET_free (order_id); 227 } 228 GNUNET_break (MHD_YES == 229 MHD_add_response_header (resp, 230 ANASTASIS_HTTP_HEADER_TALER, 231 hdr)); 232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 233 "TRUTH payment request made: %s\n", 234 hdr); 235 GNUNET_free (hdr); 236 } 237 tuc->resp = resp; 238 tuc->response_code = MHD_HTTP_PAYMENT_REQUIRED; 239 } 240 241 242 /** 243 * Callbacks of this type are used to serve the result of submitting a 244 * POST /private/orders request to a merchant. 245 * 246 * @param cls our `struct TruthUploadContext` 247 * @param por response details 248 */ 249 static void 250 proposal_cb (void *cls, 251 const struct TALER_MERCHANT_PostOrdersReply *por) 252 { 253 struct TruthUploadContext *tuc = cls; 254 255 tuc->po = NULL; 256 GNUNET_CONTAINER_DLL_remove (tuc_head, 257 tuc_tail, 258 tuc); 259 MHD_resume_connection (tuc->connection); 260 AH_trigger_daemon (NULL); 261 if (MHD_HTTP_OK != por->hr.http_status) 262 { 263 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 264 "Backend returned status %u/%d\n", 265 por->hr.http_status, 266 (int) por->hr.ec); 267 GNUNET_break (0); 268 tuc->resp = TALER_MHD_MAKE_JSON_PACK ( 269 GNUNET_JSON_pack_uint64 ("code", 270 TALER_EC_ANASTASIS_GENERIC_ORDER_CREATE_BACKEND_ERROR), 271 GNUNET_JSON_pack_string ("hint", 272 "Failed to setup order with merchant backend"), 273 GNUNET_JSON_pack_uint64 ("backend-ec", 274 por->hr.ec), 275 GNUNET_JSON_pack_uint64 ("backend-http-status", 276 por->hr.http_status), 277 GNUNET_JSON_pack_allow_null ( 278 GNUNET_JSON_pack_object_incref ("backend-reply", 279 (json_t *) por->hr.reply))); 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 osr order status 292 */ 293 static void 294 check_payment_cb (void *cls, 295 const struct TALER_MERCHANT_OrderStatusResponse *osr) 296 { 297 struct TruthUploadContext *tuc = cls; 298 const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr; 299 300 tuc->cpo = NULL; 301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 302 "Checking backend order status returned %u\n", 303 hr->http_status); 304 switch (hr->http_status) 305 { 306 case 0: 307 /* Likely timeout, complain! */ 308 tuc->response_code = MHD_HTTP_GATEWAY_TIMEOUT; 309 tuc->resp = TALER_MHD_make_error ( 310 TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT, 311 NULL); 312 break; 313 case MHD_HTTP_OK: 314 switch (osr->details.ok.status) 315 { 316 case TALER_MERCHANT_OSC_PAID: 317 { 318 enum GNUNET_DB_QueryStatus qs; 319 unsigned int years; 320 struct GNUNET_TIME_Relative paid_until; 321 const json_t *contract; 322 struct TALER_Amount amount; 323 struct GNUNET_JSON_Specification cspec[] = { 324 TALER_JSON_spec_amount_any ("amount", 325 &amount), 326 GNUNET_JSON_spec_end () 327 }; 328 329 contract = osr->details.ok.details.paid.contract_terms; 330 if (GNUNET_OK != 331 GNUNET_JSON_parse (contract, 332 cspec, 333 NULL, NULL)) 334 { 335 GNUNET_break (0); 336 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 337 tuc->resp = TALER_MHD_make_error ( 338 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 339 "contract terms in database are malformed"); 340 break; 341 } 342 years = TALER_amount_divide2 (&amount, 343 &AH_truth_upload_fee); 344 paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 345 years); 346 /* add 1 week grace period, otherwise if a user 347 wants to pay for 1 year, the first seconds 348 would have passed between making the payment 349 and our subsequent check if +1 year was 350 paid... So we actually say 1 year = 52 weeks 351 on the server, while the client calculates 352 with 365 days. */ 353 paid_until = GNUNET_TIME_relative_add (paid_until, 354 GNUNET_TIME_UNIT_WEEKS); 355 qs = db->record_truth_upload_payment ( 356 db->cls, 357 &tuc->truth_uuid, 358 &osr->details.ok.details.paid.deposit_total, 359 paid_until); 360 if (qs <= 0) 361 { 362 GNUNET_break (0); 363 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 364 tuc->resp = TALER_MHD_make_error ( 365 TALER_EC_GENERIC_DB_STORE_FAILED, 366 "record_truth_upload_payment"); 367 break; 368 } 369 } 370 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 371 "Payment confirmed, resuming upload\n"); 372 break; 373 case TALER_MERCHANT_OSC_UNPAID: 374 case TALER_MERCHANT_OSC_CLAIMED: 375 make_payment_request (tuc); 376 break; 377 } 378 break; 379 case MHD_HTTP_UNAUTHORIZED: 380 /* Configuration issue, complain! */ 381 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 382 tuc->resp = TALER_MHD_MAKE_JSON_PACK ( 383 GNUNET_JSON_pack_uint64 ("code", 384 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED), 385 GNUNET_JSON_pack_string ("hint", 386 TALER_ErrorCode_get_hint ( 387 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED)), 388 GNUNET_JSON_pack_uint64 ("backend-ec", 389 hr->ec), 390 GNUNET_JSON_pack_uint64 ("backend-http-status", 391 hr->http_status), 392 GNUNET_JSON_pack_allow_null ( 393 GNUNET_JSON_pack_object_incref ("backend-reply", 394 (json_t *) hr->reply))); 395 GNUNET_assert (NULL != tuc->resp); 396 break; 397 case MHD_HTTP_NOT_FOUND: 398 /* Setup fresh order */ 399 { 400 static const char *no_uuids[1] = { NULL }; 401 char *order_id; 402 json_t *order; 403 404 order_id = GNUNET_STRINGS_data_to_string_alloc ( 405 &tuc->truth_uuid, 406 sizeof(tuc->truth_uuid)); 407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 408 "%u, setting up fresh order %s\n", 409 MHD_HTTP_NOT_FOUND, 410 order_id); 411 order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s}", 412 "amount", 413 TALER_JSON_from_amount (&tuc->upload_fee), 414 "summary", 415 "Anastasis challenge storage fee", 416 "products", 417 "description", "challenge storage fee", 418 "quantity", (json_int_t) tuc->years_to_pay, 419 "unit", "years", 420 "order_id", 421 order_id); 422 GNUNET_free (order_id); 423 tuc->po = TALER_MERCHANT_orders_post2 (AH_ctx, 424 AH_backend_url, 425 order, 426 GNUNET_TIME_UNIT_ZERO, 427 NULL, /* no payment target */ 428 0, 429 NULL, /* no inventory products */ 430 0, 431 no_uuids, /* no uuids */ 432 false, /* do NOT require claim token */ 433 &proposal_cb, 434 tuc); 435 AH_trigger_curl (); 436 json_decref (order); 437 return; 438 } 439 default: 440 /* Unexpected backend response */ 441 tuc->response_code = MHD_HTTP_BAD_GATEWAY; 442 tuc->resp = TALER_MHD_MAKE_JSON_PACK ( 443 GNUNET_JSON_pack_uint64 ("code", 444 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR), 445 GNUNET_JSON_pack_string ("hint", 446 TALER_ErrorCode_get_hint ( 447 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR)), 448 GNUNET_JSON_pack_uint64 ("backend-ec", 449 (json_int_t) hr->ec), 450 GNUNET_JSON_pack_uint64 ("backend-http-status", 451 (json_int_t) hr->http_status), 452 GNUNET_JSON_pack_allow_null ( 453 GNUNET_JSON_pack_object_incref ("backend-reply", 454 (json_t *) hr->reply))); 455 break; 456 } 457 GNUNET_CONTAINER_DLL_remove (tuc_head, 458 tuc_tail, 459 tuc); 460 MHD_resume_connection (tuc->connection); 461 AH_trigger_daemon (NULL); 462 } 463 464 465 /** 466 * Helper function used to ask our backend to begin processing a 467 * payment for the truth upload. May perform asynchronous operations 468 * by suspending the connection if required. 469 * 470 * @param tuc context to begin payment for. 471 * @return MHD status code 472 */ 473 static MHD_RESULT 474 begin_payment (struct TruthUploadContext *tuc) 475 { 476 char *order_id; 477 struct GNUNET_TIME_Relative timeout; 478 479 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 480 "Checking backend order status...\n"); 481 timeout = GNUNET_TIME_absolute_get_remaining (tuc->timeout); 482 order_id = GNUNET_STRINGS_data_to_string_alloc ( 483 &tuc->truth_uuid, 484 sizeof (tuc->truth_uuid)); 485 tuc->cpo = TALER_MERCHANT_merchant_order_get (AH_ctx, 486 AH_backend_url, 487 order_id, 488 NULL /* our payments are NOT session-bound */ 489 , 490 timeout, 491 &check_payment_cb, 492 tuc); 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_CONTAINER_DLL_insert (tuc_head, 503 tuc_tail, 504 tuc); 505 MHD_suspend_connection (tuc->connection); 506 return MHD_YES; 507 } 508 509 510 MHD_RESULT 511 AH_handler_truth_post ( 512 struct MHD_Connection *connection, 513 struct TM_HandlerContext *hc, 514 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 515 const char *truth_data, 516 size_t *truth_data_size) 517 { 518 struct TruthUploadContext *tuc = hc->ctx; 519 MHD_RESULT ret; 520 int res; 521 struct ANASTASIS_CRYPTO_EncryptedKeyShareP key_share_data; 522 void *encrypted_truth; 523 size_t encrypted_truth_size; 524 const char *truth_mime = NULL; 525 const char *type; 526 uint32_t storage_years; 527 struct GNUNET_TIME_Timestamp paid_until 528 = GNUNET_TIME_UNIT_ZERO_TS; 529 struct GNUNET_JSON_Specification spec[] = { 530 GNUNET_JSON_spec_fixed_auto ("key_share_data", 531 &key_share_data), 532 GNUNET_JSON_spec_string ("type", 533 &type), 534 GNUNET_JSON_spec_varsize ("encrypted_truth", 535 &encrypted_truth, 536 &encrypted_truth_size), 537 GNUNET_JSON_spec_mark_optional ( 538 GNUNET_JSON_spec_string ("truth_mime", 539 &truth_mime), 540 NULL), 541 GNUNET_JSON_spec_uint32 ("storage_duration_years", 542 &storage_years), 543 GNUNET_JSON_spec_end () 544 }; 545 546 if (NULL == tuc) 547 { 548 tuc = GNUNET_new (struct TruthUploadContext); 549 tuc->connection = connection; 550 tuc->truth_uuid = *truth_uuid; 551 hc->ctx = tuc; 552 hc->cc = &cleanup_truth_post; 553 TALER_MHD_check_content_length (connection, 554 AH_upload_limit_mb * 1024LLU * 1024LLU); 555 tuc->timeout = GNUNET_TIME_relative_to_absolute ( 556 GNUNET_TIME_UNIT_SECONDS); 557 TALER_MHD_parse_request_timeout (connection, 558 &tuc->timeout); 559 } /* end 'if (NULL == tuc)' */ 560 561 if (NULL != tuc->resp) 562 { 563 /* We generated a response asynchronously, queue that */ 564 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 565 "Returning asynchronously generated response with HTTP status %u\n", 566 tuc->response_code); 567 ret = MHD_queue_response (connection, 568 tuc->response_code, 569 tuc->resp); 570 GNUNET_break (MHD_YES == ret); 571 MHD_destroy_response (tuc->resp); 572 tuc->resp = NULL; 573 return ret; 574 } 575 576 if (NULL == tuc->json) 577 { 578 res = TALER_MHD_parse_post_json (connection, 579 &tuc->post_ctx, 580 truth_data, 581 truth_data_size, 582 &tuc->json); 583 if (GNUNET_SYSERR == res) 584 { 585 GNUNET_break (0); 586 return MHD_NO; 587 } 588 if ( (GNUNET_NO == res) || 589 (NULL == tuc->json) ) 590 return MHD_YES; 591 } 592 res = TALER_MHD_parse_json_data (connection, 593 tuc->json, 594 spec); 595 if (GNUNET_SYSERR == res) 596 { 597 GNUNET_break (0); 598 return MHD_NO; /* hard failure */ 599 } 600 if (GNUNET_NO == res) 601 { 602 GNUNET_break_op (0); 603 return MHD_YES; /* failure */ 604 } 605 606 /* check method is supported */ 607 if ( (0 != strcmp ("question", 608 type)) && 609 (NULL == 610 ANASTASIS_authorization_plugin_load (type, 611 db, 612 AH_cfg)) ) 613 { 614 GNUNET_JSON_parse_free (spec); 615 return TALER_MHD_reply_with_error (connection, 616 MHD_HTTP_BAD_REQUEST, 617 TALER_EC_ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED, 618 type); 619 } 620 621 if (storage_years > ANASTASIS_MAX_YEARS_STORAGE) 622 { 623 GNUNET_break_op (0); 624 return TALER_MHD_reply_with_error (connection, 625 MHD_HTTP_BAD_REQUEST, 626 TALER_EC_GENERIC_PARAMETER_MALFORMED, 627 "storage_duration_years"); 628 } 629 if (0 == storage_years) 630 storage_years = 1; 631 632 if (! TALER_amount_is_zero (&AH_truth_upload_fee)) 633 { 634 struct GNUNET_TIME_Timestamp desired_until; 635 enum GNUNET_DB_QueryStatus qs; 636 637 desired_until 638 = GNUNET_TIME_relative_to_timestamp ( 639 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 640 storage_years)); 641 qs = db->check_truth_upload_paid (db->cls, 642 truth_uuid, 643 &paid_until); 644 if (qs < 0) 645 return TALER_MHD_reply_with_error (connection, 646 MHD_HTTP_INTERNAL_SERVER_ERROR, 647 TALER_EC_GENERIC_DB_FETCH_FAILED, 648 NULL); 649 if ( (0 == qs) || 650 (GNUNET_TIME_timestamp_cmp (paid_until, 651 <, 652 desired_until) ) ) 653 { 654 struct GNUNET_TIME_Relative rem; 655 656 if (GNUNET_TIME_absolute_is_past (paid_until.abs_time)) 657 paid_until = GNUNET_TIME_timestamp_get (); 658 rem = GNUNET_TIME_absolute_get_difference (paid_until.abs_time, 659 desired_until.abs_time); 660 tuc->years_to_pay = rem.rel_value_us 661 / GNUNET_TIME_UNIT_YEARS.rel_value_us; 662 if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) 663 tuc->years_to_pay++; 664 if (0 > 665 TALER_amount_multiply (&tuc->upload_fee, 666 &AH_truth_upload_fee, 667 tuc->years_to_pay)) 668 { 669 GNUNET_break_op (0); 670 return TALER_MHD_reply_with_error (connection, 671 MHD_HTTP_BAD_REQUEST, 672 TALER_EC_GENERIC_PARAMETER_MALFORMED, 673 "storage_duration_years"); 674 } 675 if (! TALER_amount_is_zero (&tuc->upload_fee)) 676 { 677 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 678 "Truth upload payment required (%d)!\n", 679 qs); 680 return begin_payment (tuc); 681 } 682 } 683 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 684 "TRUTH paid until %s (%d)!\n", 685 GNUNET_TIME_relative2s ( 686 GNUNET_TIME_absolute_get_remaining ( 687 paid_until.abs_time), 688 GNUNET_YES), 689 qs); 690 } 691 else 692 { 693 paid_until 694 = GNUNET_TIME_relative_to_timestamp ( 695 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 696 ANASTASIS_MAX_YEARS_STORAGE)); 697 } 698 699 700 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 701 "Storing truth %s until %s!\n", 702 TALER_B2S (truth_uuid), 703 GNUNET_TIME_timestamp2s (paid_until)); 704 { 705 enum GNUNET_DB_QueryStatus qs; 706 707 qs = db->store_truth (db->cls, 708 truth_uuid, 709 &key_share_data, 710 (NULL == truth_mime) 711 ? "" 712 : truth_mime, 713 encrypted_truth, 714 encrypted_truth_size, 715 type, 716 GNUNET_TIME_absolute_get_remaining ( 717 paid_until.abs_time)); 718 switch (qs) 719 { 720 case GNUNET_DB_STATUS_HARD_ERROR: 721 case GNUNET_DB_STATUS_SOFT_ERROR: 722 GNUNET_break (0); 723 GNUNET_JSON_parse_free (spec); 724 return TALER_MHD_reply_with_error (connection, 725 MHD_HTTP_INTERNAL_SERVER_ERROR, 726 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 727 "store_truth"); 728 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 729 { 730 void *xtruth; 731 size_t xtruth_size; 732 char *xtruth_mime; 733 char *xmethod; 734 bool ok = false; 735 736 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 737 db->get_escrow_challenge (db->cls, 738 truth_uuid, 739 &xtruth, 740 &xtruth_size, 741 &xtruth_mime, 742 &xmethod)) 743 { 744 ok = ( (xtruth_size == encrypted_truth_size) && 745 (0 == strcmp (xmethod, 746 type)) && 747 (0 == strcmp (((NULL == truth_mime) ? "" : truth_mime), 748 ((NULL == xtruth_mime) ? "" : xtruth_mime))) && 749 (0 == memcmp (xtruth, 750 encrypted_truth, 751 xtruth_size)) ); 752 GNUNET_free (encrypted_truth); 753 GNUNET_free (xtruth_mime); 754 GNUNET_free (xmethod); 755 } 756 if (! ok) 757 { 758 GNUNET_JSON_parse_free (spec); 759 760 return TALER_MHD_reply_with_error (connection, 761 MHD_HTTP_CONFLICT, 762 TALER_EC_ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS, 763 NULL); 764 } 765 /* idempotency detected, intentional fall through! */ 766 } 767 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 768 { 769 struct MHD_Response *resp; 770 771 GNUNET_JSON_parse_free (spec); 772 resp = MHD_create_response_from_buffer (0, 773 NULL, 774 MHD_RESPMEM_PERSISTENT); 775 TALER_MHD_add_global_headers (resp, 776 false); 777 ret = MHD_queue_response (connection, 778 MHD_HTTP_NO_CONTENT, 779 resp); 780 MHD_destroy_response (resp); 781 GNUNET_break (MHD_YES == ret); 782 return ret; 783 } 784 } 785 } 786 GNUNET_JSON_parse_free (spec); 787 GNUNET_break (0); 788 return MHD_NO; 789 }