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