anastasis-httpd_truth-upload.c (27150B)
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_insert_truth_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 "insert_truth_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 if (NULL == order) 427 { 428 GNUNET_break (0); 429 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 430 tuc->resp = TALER_MHD_make_error ( 431 TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, 432 "could not create order"); 433 break; 434 } 435 tuc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx, 436 AH_backend_url, 437 order); 438 GNUNET_assert (NULL != tuc->po); 439 GNUNET_assert ( 440 GNUNET_OK == 441 TALER_MERCHANT_post_private_orders_set_options ( 442 tuc->po, 443 TALER_MERCHANT_post_private_orders_option_create_token (false))); 444 GNUNET_assert (TALER_EC_NONE == 445 TALER_MERCHANT_post_private_orders_start (tuc->po, 446 &proposal_cb, 447 tuc)); 448 AH_trigger_curl (); 449 json_decref (order); 450 return; 451 } 452 default: 453 /* Unexpected backend response */ 454 tuc->response_code = MHD_HTTP_BAD_GATEWAY; 455 tuc->resp = TALER_MHD_MAKE_JSON_PACK ( 456 GNUNET_JSON_pack_uint64 ("code", 457 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR), 458 GNUNET_JSON_pack_string ("hint", 459 TALER_ErrorCode_get_hint ( 460 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR)), 461 GNUNET_JSON_pack_uint64 ("backend-ec", 462 (json_int_t) hr->ec), 463 GNUNET_JSON_pack_uint64 ("backend-http-status", 464 (json_int_t) hr->http_status), 465 GNUNET_JSON_pack_allow_null ( 466 GNUNET_JSON_pack_object_incref ("backend-reply", 467 (json_t *) hr->reply))); 468 break; 469 } 470 GNUNET_CONTAINER_DLL_remove (tuc_head, 471 tuc_tail, 472 tuc); 473 MHD_resume_connection (tuc->connection); 474 AH_trigger_daemon (NULL); 475 } 476 477 478 /** 479 * Helper function used to ask our backend to begin processing a 480 * payment for the truth upload. May perform asynchronous operations 481 * by suspending the connection if required. 482 * 483 * @param tuc context to begin payment for. 484 * @return MHD status code 485 */ 486 static enum MHD_Result 487 begin_payment (struct TruthUploadContext *tuc) 488 { 489 char *order_id; 490 struct GNUNET_TIME_Relative timeout; 491 492 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 493 "Checking backend order status...\n"); 494 timeout = GNUNET_TIME_absolute_get_remaining (tuc->timeout); 495 order_id = GNUNET_STRINGS_data_to_string_alloc ( 496 &tuc->truth_uuid, 497 sizeof (tuc->truth_uuid)); 498 tuc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx, 499 AH_backend_url, 500 order_id); 501 GNUNET_free (order_id); 502 if (NULL == tuc->cpo) 503 { 504 GNUNET_break (0); 505 return TALER_MHD_reply_with_error (tuc->connection, 506 MHD_HTTP_INTERNAL_SERVER_ERROR, 507 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED, 508 "Could not check order status"); 509 } 510 GNUNET_assert (GNUNET_OK == 511 TALER_MERCHANT_get_private_order_set_options ( 512 tuc->cpo, 513 TALER_MERCHANT_get_private_order_option_timeout (timeout))); 514 if (TALER_EC_NONE != 515 TALER_MERCHANT_get_private_order_start (tuc->cpo, 516 &check_payment_cb, 517 tuc)) 518 { 519 GNUNET_break (0); 520 return TALER_MHD_reply_with_error (tuc->connection, 521 MHD_HTTP_INTERNAL_SERVER_ERROR, 522 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED, 523 "Could not check order status"); 524 } 525 GNUNET_CONTAINER_DLL_insert (tuc_head, 526 tuc_tail, 527 tuc); 528 MHD_suspend_connection (tuc->connection); 529 return MHD_YES; 530 } 531 532 533 enum MHD_Result 534 AH_handler_truth_post ( 535 struct MHD_Connection *connection, 536 struct TM_HandlerContext *hc, 537 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 538 const char *truth_data, 539 size_t *truth_data_size) 540 { 541 struct TruthUploadContext *tuc = hc->ctx; 542 enum MHD_Result ret; 543 int res; 544 struct ANASTASIS_CRYPTO_EncryptedKeyShareP key_share_data; 545 void *encrypted_truth; 546 size_t encrypted_truth_size; 547 const char *truth_mime = NULL; 548 const char *type; 549 uint32_t storage_years; 550 struct GNUNET_TIME_Timestamp paid_until 551 = GNUNET_TIME_UNIT_ZERO_TS; 552 struct GNUNET_JSON_Specification spec[] = { 553 GNUNET_JSON_spec_fixed_auto ("key_share_data", 554 &key_share_data), 555 GNUNET_JSON_spec_string ("type", 556 &type), 557 GNUNET_JSON_spec_varsize ("encrypted_truth", 558 &encrypted_truth, 559 &encrypted_truth_size), 560 GNUNET_JSON_spec_mark_optional ( 561 GNUNET_JSON_spec_string ("truth_mime", 562 &truth_mime), 563 NULL), 564 GNUNET_JSON_spec_uint32 ("storage_duration_years", 565 &storage_years), 566 GNUNET_JSON_spec_end () 567 }; 568 569 if (NULL == tuc) 570 { 571 tuc = GNUNET_new (struct TruthUploadContext); 572 tuc->connection = connection; 573 tuc->truth_uuid = *truth_uuid; 574 hc->ctx = tuc; 575 hc->cc = &cleanup_truth_post; 576 TALER_MHD_check_content_length (connection, 577 AH_upload_limit_mb * 1024LLU * 1024LLU); 578 tuc->timeout = GNUNET_TIME_relative_to_absolute ( 579 GNUNET_TIME_UNIT_SECONDS); 580 TALER_MHD_parse_request_timeout (connection, 581 &tuc->timeout); 582 } /* end 'if (NULL == tuc)' */ 583 584 if (NULL != tuc->resp) 585 { 586 /* We generated a response asynchronously, queue that */ 587 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 588 "Returning asynchronously generated response with HTTP status %u\n", 589 tuc->response_code); 590 ret = MHD_queue_response (connection, 591 tuc->response_code, 592 tuc->resp); 593 GNUNET_break (MHD_YES == ret); 594 MHD_destroy_response (tuc->resp); 595 tuc->resp = NULL; 596 return ret; 597 } 598 599 if (NULL == tuc->json) 600 { 601 res = TALER_MHD_parse_post_json (connection, 602 &tuc->post_ctx, 603 truth_data, 604 truth_data_size, 605 &tuc->json); 606 if (GNUNET_SYSERR == res) 607 { 608 GNUNET_break (0); 609 return MHD_NO; 610 } 611 if ( (GNUNET_NO == res) || 612 (NULL == tuc->json) ) 613 return MHD_YES; 614 } 615 res = TALER_MHD_parse_json_data (connection, 616 tuc->json, 617 spec); 618 if (GNUNET_SYSERR == res) 619 { 620 GNUNET_break (0); 621 return MHD_NO; /* hard failure */ 622 } 623 if (GNUNET_NO == res) 624 { 625 GNUNET_break_op (0); 626 return MHD_YES; /* failure */ 627 } 628 629 /* check method is supported */ 630 if ( (0 != strcmp ("question", 631 type)) && 632 (NULL == 633 ANASTASIS_authorization_plugin_load (type, 634 AH_cfg)) ) 635 { 636 GNUNET_JSON_parse_free (spec); 637 return TALER_MHD_reply_with_error (connection, 638 MHD_HTTP_BAD_REQUEST, 639 TALER_EC_ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED, 640 type); 641 } 642 643 if (storage_years > ANASTASIS_MAX_YEARS_STORAGE) 644 { 645 GNUNET_break_op (0); 646 GNUNET_JSON_parse_free (spec); 647 return TALER_MHD_reply_with_error (connection, 648 MHD_HTTP_BAD_REQUEST, 649 TALER_EC_GENERIC_PARAMETER_MALFORMED, 650 "storage_duration_years"); 651 } 652 if (0 == storage_years) 653 storage_years = 1; 654 655 if (! TALER_amount_is_zero (&AH_truth_upload_fee)) 656 { 657 struct GNUNET_TIME_Timestamp desired_until; 658 enum GNUNET_DB_QueryStatus qs; 659 660 desired_until 661 = GNUNET_TIME_relative_to_timestamp ( 662 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 663 storage_years)); 664 qs = ANASTASIS_DB_get_truth_payment ( 665 truth_uuid, 666 &paid_until); 667 if (qs < 0) 668 { 669 GNUNET_JSON_parse_free (spec); 670 return TALER_MHD_reply_with_error (connection, 671 MHD_HTTP_INTERNAL_SERVER_ERROR, 672 TALER_EC_GENERIC_DB_FETCH_FAILED, 673 NULL); 674 } 675 if ( (0 == qs) || 676 (GNUNET_TIME_timestamp_cmp (paid_until, 677 <, 678 desired_until) ) ) 679 { 680 struct GNUNET_TIME_Relative rem; 681 682 if (GNUNET_TIME_absolute_is_past (paid_until.abs_time)) 683 paid_until = GNUNET_TIME_timestamp_get (); 684 rem = GNUNET_TIME_absolute_get_difference (paid_until.abs_time, 685 desired_until.abs_time); 686 tuc->years_to_pay = rem.rel_value_us 687 / GNUNET_TIME_UNIT_YEARS.rel_value_us; 688 if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) 689 tuc->years_to_pay++; 690 if (0 > 691 TALER_amount_multiply (&tuc->upload_fee, 692 &AH_truth_upload_fee, 693 tuc->years_to_pay)) 694 { 695 GNUNET_break_op (0); 696 GNUNET_JSON_parse_free (spec); 697 return TALER_MHD_reply_with_error (connection, 698 MHD_HTTP_BAD_REQUEST, 699 TALER_EC_GENERIC_PARAMETER_MALFORMED, 700 "storage_duration_years"); 701 } 702 if (! TALER_amount_is_zero (&tuc->upload_fee)) 703 { 704 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 705 "Truth upload payment required (%d)!\n", 706 qs); 707 /* `spec' is re-parsed from tuc->json when MHD resumes us, and 708 begin_payment() only touches `tuc', so release it now. */ 709 GNUNET_JSON_parse_free (spec); 710 return begin_payment (tuc); 711 } 712 } 713 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 714 "TRUTH paid until %s (%d)!\n", 715 GNUNET_TIME_relative2s ( 716 GNUNET_TIME_absolute_get_remaining ( 717 paid_until.abs_time), 718 GNUNET_YES), 719 qs); 720 } 721 else 722 { 723 paid_until 724 = GNUNET_TIME_relative_to_timestamp ( 725 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 726 ANASTASIS_MAX_YEARS_STORAGE)); 727 } 728 729 730 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 731 "Storing truth %s until %s!\n", 732 TALER_B2S (truth_uuid), 733 GNUNET_TIME_timestamp2s (paid_until)); 734 { 735 enum GNUNET_DB_QueryStatus qs; 736 737 qs = ANASTASIS_DB_insert_truth ( 738 truth_uuid, 739 &key_share_data, 740 (NULL == truth_mime) 741 ? "" 742 : truth_mime, 743 encrypted_truth, 744 encrypted_truth_size, 745 type, 746 GNUNET_TIME_absolute_get_remaining ( 747 paid_until.abs_time)); 748 switch (qs) 749 { 750 case GNUNET_DB_STATUS_HARD_ERROR: 751 case GNUNET_DB_STATUS_SOFT_ERROR: 752 GNUNET_break (0); 753 GNUNET_JSON_parse_free (spec); 754 return TALER_MHD_reply_with_error (connection, 755 MHD_HTTP_INTERNAL_SERVER_ERROR, 756 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 757 "insert_truth"); 758 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 759 { 760 void *xtruth; 761 size_t xtruth_size; 762 char *xtruth_mime; 763 char *xmethod; 764 bool ok = false; 765 766 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 767 ANASTASIS_DB_get_truth ( 768 truth_uuid, 769 &xtruth, 770 &xtruth_size, 771 &xtruth_mime, 772 &xmethod)) 773 { 774 ok = ( (xtruth_size == encrypted_truth_size) && 775 (0 == strcmp (xmethod, 776 type)) && 777 (0 == strcmp (((NULL == truth_mime) ? "" : truth_mime), 778 ((NULL == xtruth_mime) ? "" : xtruth_mime))) && 779 (0 == memcmp (xtruth, 780 encrypted_truth, 781 xtruth_size)) ); 782 /* NOTE: do NOT free encrypted_truth here; it is owned by `spec` 783 and released by GNUNET_JSON_parse_free (spec) below. Freeing it 784 here caused a double free on the idempotent fall-through. */ 785 GNUNET_free (xtruth); 786 GNUNET_free (xtruth_mime); 787 GNUNET_free (xmethod); 788 } 789 if (! ok) 790 { 791 GNUNET_JSON_parse_free (spec); 792 793 return TALER_MHD_reply_with_error (connection, 794 MHD_HTTP_CONFLICT, 795 TALER_EC_ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS, 796 NULL); 797 } 798 /* idempotency detected, intentional fall through! */ 799 } 800 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 801 { 802 struct MHD_Response *resp; 803 804 GNUNET_JSON_parse_free (spec); 805 resp = MHD_create_response_from_buffer (0, 806 NULL, 807 MHD_RESPMEM_PERSISTENT); 808 TALER_MHD_add_global_headers (resp, 809 false); 810 ret = MHD_queue_response (connection, 811 MHD_HTTP_NO_CONTENT, 812 resp); 813 MHD_destroy_response (resp); 814 GNUNET_break (MHD_YES == ret); 815 return ret; 816 } 817 } 818 } 819 GNUNET_JSON_parse_free (spec); 820 GNUNET_break (0); 821 return MHD_NO; 822 }