anastasis-httpd_truth-upload.c (27650B)
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, one entry per currency the 100 * wallet may choose to settle in. 101 */ 102 struct TALER_AmountList upload_fees; 103 104 /** 105 * HTTP response code to use on resume, if resp is set. 106 */ 107 unsigned int response_code; 108 109 /** 110 * For how many years must the customer still pay? 111 */ 112 unsigned int years_to_pay; 113 114 }; 115 116 117 /** 118 * Head of linked list over all truth upload processes 119 */ 120 static struct TruthUploadContext *tuc_head; 121 122 /** 123 * Tail of linked list over all truth upload processes 124 */ 125 static struct TruthUploadContext *tuc_tail; 126 127 128 void 129 AH_truth_upload_shutdown (void) 130 { 131 struct TruthUploadContext *tuc; 132 133 while (NULL != (tuc = tuc_head)) 134 { 135 GNUNET_CONTAINER_DLL_remove (tuc_head, 136 tuc_tail, 137 tuc); 138 if (NULL != tuc->cpo) 139 { 140 TALER_MERCHANT_get_private_order_cancel (tuc->cpo); 141 tuc->cpo = NULL; 142 } 143 if (NULL != tuc->po) 144 { 145 TALER_MERCHANT_post_private_orders_cancel (tuc->po); 146 tuc->po = NULL; 147 } 148 MHD_resume_connection (tuc->connection); 149 } 150 } 151 152 153 /** 154 * Function called to clean up a `struct TruthUploadContext`. 155 * 156 * @param hc general handler context 157 */ 158 static void 159 cleanup_truth_post (struct TM_HandlerContext *hc) 160 { 161 struct TruthUploadContext *tuc = hc->ctx; 162 163 TALER_MHD_parse_post_cleanup_callback (tuc->post_ctx); 164 if (NULL != tuc->po) 165 TALER_MERCHANT_post_private_orders_cancel (tuc->po); 166 if (NULL != tuc->cpo) 167 TALER_MERCHANT_get_private_order_cancel (tuc->cpo); 168 if (NULL != tuc->resp) 169 MHD_destroy_response (tuc->resp); 170 if (NULL != tuc->json) 171 json_decref (tuc->json); 172 TALER_amount_list_free (&tuc->upload_fees); 173 GNUNET_free (tuc); 174 } 175 176 177 /** 178 * Transmit a payment request for @a tuc. 179 * 180 * @param tuc upload context to generate payment request for 181 */ 182 static void 183 make_payment_request (struct TruthUploadContext *tuc) 184 { 185 struct MHD_Response *resp; 186 187 /* request payment via Taler */ 188 resp = MHD_create_response_from_buffer (0, 189 NULL, 190 MHD_RESPMEM_PERSISTENT); 191 GNUNET_assert (NULL != resp); 192 TALER_MHD_add_global_headers (resp, 193 false); 194 { 195 char *hdr; 196 const char *pfx; 197 const char *hn; 198 199 if (0 == strncasecmp ("https://", 200 AH_backend_url, 201 strlen ("https://"))) 202 { 203 pfx = "taler://"; 204 hn = &AH_backend_url[strlen ("https://")]; 205 } 206 else if (0 == strncasecmp ("http://", 207 AH_backend_url, 208 strlen ("http://"))) 209 { 210 pfx = "taler+http://"; 211 hn = &AH_backend_url[strlen ("http://")]; 212 } 213 else 214 { 215 /* This invariant holds as per check in anastasis-httpd.c */ 216 GNUNET_assert (0); 217 } 218 /* This invariant holds as per check in anastasis-httpd.c */ 219 GNUNET_assert (0 != strlen (hn)); 220 { 221 char *order_id; 222 223 order_id = GNUNET_STRINGS_data_to_string_alloc ( 224 &tuc->truth_uuid, 225 sizeof (tuc->truth_uuid)); 226 GNUNET_asprintf (&hdr, 227 "%spay/%s%s/", 228 pfx, 229 hn, 230 order_id); 231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 232 "Returning %u %s\n", 233 MHD_HTTP_PAYMENT_REQUIRED, 234 order_id); 235 GNUNET_free (order_id); 236 } 237 GNUNET_break (MHD_YES == 238 MHD_add_response_header (resp, 239 ANASTASIS_HTTP_HEADER_TALER, 240 hdr)); 241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 242 "TRUTH payment request made: %s\n", 243 hdr); 244 GNUNET_free (hdr); 245 } 246 tuc->resp = resp; 247 tuc->response_code = MHD_HTTP_PAYMENT_REQUIRED; 248 } 249 250 251 /** 252 * Callbacks of this type are used to serve the result of submitting a 253 * POST /private/orders request to a merchant. 254 * 255 * @param cls our `struct TruthUploadContext` 256 * @param por response details 257 */ 258 static void 259 proposal_cb (struct TruthUploadContext *tuc, 260 const struct TALER_MERCHANT_PostPrivateOrdersResponse *por) 261 { 262 263 tuc->po = NULL; 264 GNUNET_CONTAINER_DLL_remove (tuc_head, 265 tuc_tail, 266 tuc); 267 MHD_resume_connection (tuc->connection); 268 AH_trigger_daemon (NULL); 269 if (MHD_HTTP_OK != por->hr.http_status) 270 { 271 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 272 "Backend returned status %u/%d\n", 273 por->hr.http_status, 274 (int) por->hr.ec); 275 GNUNET_break (0); 276 tuc->resp = TALER_MHD_MAKE_JSON_PACK ( 277 GNUNET_JSON_pack_uint64 ("code", 278 TALER_EC_ANASTASIS_GENERIC_ORDER_CREATE_BACKEND_ERROR), 279 GNUNET_JSON_pack_string ("hint", 280 "Failed to setup order with merchant backend"), 281 GNUNET_JSON_pack_uint64 ("backend-ec", 282 por->hr.ec), 283 GNUNET_JSON_pack_uint64 ("backend-http-status", 284 por->hr.http_status), 285 GNUNET_JSON_pack_allow_null ( 286 GNUNET_JSON_pack_object_incref ("backend-reply", 287 (json_t *) por->hr.reply))); 288 tuc->response_code = MHD_HTTP_BAD_GATEWAY; 289 return; 290 } 291 make_payment_request (tuc); 292 } 293 294 295 /** 296 * Callback to process a GET /check-payment request 297 * 298 * @param cls our `struct PolicyUploadContext` 299 * @param osr order status 300 */ 301 static void 302 check_payment_cb (struct TruthUploadContext *tuc, 303 const struct TALER_MERCHANT_GetPrivateOrderResponse *osr) 304 { 305 const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr; 306 307 tuc->cpo = NULL; 308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 309 "Checking backend order status returned %u\n", 310 hr->http_status); 311 switch (hr->http_status) 312 { 313 case 0: 314 /* Likely timeout, complain! */ 315 tuc->response_code = MHD_HTTP_GATEWAY_TIMEOUT; 316 tuc->resp = TALER_MHD_make_error ( 317 TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT, 318 NULL); 319 break; 320 case MHD_HTTP_OK: 321 switch (osr->details.ok.status) 322 { 323 case TALER_MERCHANT_OSC_PAID: 324 { 325 enum GNUNET_DB_QueryStatus qs; 326 unsigned int years; 327 struct GNUNET_TIME_Relative paid_until; 328 struct TALER_Amount amount; 329 const struct TALER_Amount *fee; 330 331 if (GNUNET_OK != 332 AH_paid_amount (osr, 333 &amount)) 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 /* Divide by the fee in the currency actually paid: with several 343 on offer, "the" fee is not well defined. */ 344 fee = TALER_amount_list_find (&AH_truth_upload_fees, 345 amount.currency); 346 if (NULL == fee) 347 { 348 GNUNET_break (0); 349 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 350 tuc->resp = TALER_MHD_make_error ( 351 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR, 352 "order was paid in a currency this provider does not offer"); 353 break; 354 } 355 years = TALER_amount_divide2 (&amount, 356 fee); 357 paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 358 years); 359 /* add 1 week grace period, otherwise if a user 360 wants to pay for 1 year, the first seconds 361 would have passed between making the payment 362 and our subsequent check if +1 year was 363 paid... So we actually say 1 year = 52 weeks 364 on the server, while the client calculates 365 with 365 days. */ 366 paid_until = GNUNET_TIME_relative_add (paid_until, 367 GNUNET_TIME_UNIT_WEEKS); 368 qs = ANASTASIS_DB_insert_truth_payment ( 369 &tuc->truth_uuid, 370 &osr->details.ok.details.paid.deposit_total, 371 paid_until); 372 if (qs <= 0) 373 { 374 GNUNET_break (0); 375 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 376 tuc->resp = TALER_MHD_make_error ( 377 TALER_EC_GENERIC_DB_STORE_FAILED, 378 "insert_truth_payment"); 379 break; 380 } 381 } 382 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 383 "Payment confirmed, resuming upload\n"); 384 break; 385 case TALER_MERCHANT_OSC_UNPAID: 386 case TALER_MERCHANT_OSC_CLAIMED: 387 make_payment_request (tuc); 388 break; 389 } 390 break; 391 case MHD_HTTP_UNAUTHORIZED: 392 /* Configuration issue, complain! */ 393 tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 394 tuc->resp = TALER_MHD_MAKE_JSON_PACK ( 395 GNUNET_JSON_pack_uint64 ("code", 396 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED), 397 GNUNET_JSON_pack_string ("hint", 398 TALER_ErrorCode_get_hint ( 399 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED)), 400 GNUNET_JSON_pack_uint64 ("backend-ec", 401 hr->ec), 402 GNUNET_JSON_pack_uint64 ("backend-http-status", 403 hr->http_status), 404 GNUNET_JSON_pack_allow_null ( 405 GNUNET_JSON_pack_object_incref ("backend-reply", 406 (json_t *) hr->reply))); 407 GNUNET_assert (NULL != tuc->resp); 408 break; 409 case MHD_HTTP_NOT_FOUND: 410 /* Setup fresh order */ 411 { 412 char *order_id; 413 json_t *order; 414 415 order_id = GNUNET_STRINGS_data_to_string_alloc ( 416 &tuc->truth_uuid, 417 sizeof(tuc->truth_uuid)); 418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 419 "%u, setting up fresh order %s\n", 420 MHD_HTTP_NOT_FOUND, 421 order_id); 422 order = AH_make_order (order_id, 423 "Anastasis challenge storage fee", 424 &tuc->upload_fees); 425 GNUNET_free (order_id); 426 GNUNET_assert (0 == 427 json_object_set_new ( 428 order, 429 "products", 430 json_pack ("[{s:s,s:I,s:s}]", 431 "description", "challenge storage fee", 432 "quantity", 433 (json_int_t) tuc->years_to_pay, 434 "unit", "years"))); 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 /* GNUNET_SYSERR is unreachable: anastasis-httpd refuses to start on a 656 price list that mixes free and non-free currencies. */ 657 GNUNET_assert (GNUNET_SYSERR != 658 TALER_amount_list_check_uniform (&AH_truth_upload_fees)); 659 if (GNUNET_NO != 660 TALER_amount_list_check_uniform (&AH_truth_upload_fees)) 661 { 662 struct GNUNET_TIME_Timestamp desired_until; 663 enum GNUNET_DB_QueryStatus qs; 664 665 desired_until 666 = GNUNET_TIME_relative_to_timestamp ( 667 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 668 storage_years)); 669 qs = ANASTASIS_DB_get_truth_payment ( 670 truth_uuid, 671 &paid_until); 672 if (qs < 0) 673 { 674 GNUNET_JSON_parse_free (spec); 675 return TALER_MHD_reply_with_error (connection, 676 MHD_HTTP_INTERNAL_SERVER_ERROR, 677 TALER_EC_GENERIC_DB_FETCH_FAILED, 678 NULL); 679 } 680 if ( (0 == qs) || 681 (GNUNET_TIME_timestamp_cmp (paid_until, 682 <, 683 desired_until) ) ) 684 { 685 struct GNUNET_TIME_Relative rem; 686 687 if (GNUNET_TIME_absolute_is_past (paid_until.abs_time)) 688 paid_until = GNUNET_TIME_timestamp_get (); 689 rem = GNUNET_TIME_absolute_get_difference (paid_until.abs_time, 690 desired_until.abs_time); 691 tuc->years_to_pay = rem.rel_value_us 692 / GNUNET_TIME_UNIT_YEARS.rel_value_us; 693 if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) 694 tuc->years_to_pay++; 695 TALER_amount_list_free (&tuc->upload_fees); 696 TALER_amount_list_copy (&tuc->upload_fees, 697 &AH_truth_upload_fees); 698 if (GNUNET_OK != 699 TALER_amount_list_multiply (&tuc->upload_fees, 700 tuc->years_to_pay)) 701 { 702 GNUNET_break_op (0); 703 GNUNET_JSON_parse_free (spec); 704 return TALER_MHD_reply_with_error (connection, 705 MHD_HTTP_BAD_REQUEST, 706 TALER_EC_GENERIC_PARAMETER_MALFORMED, 707 "storage_duration_years"); 708 } 709 if (0 != tuc->upload_fees.tal_len) 710 { 711 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 712 "Truth upload payment required (%d)!\n", 713 qs); 714 /* `spec' is re-parsed from tuc->json when MHD resumes us, and 715 begin_payment() only touches `tuc', so release it now. */ 716 GNUNET_JSON_parse_free (spec); 717 return begin_payment (tuc); 718 } 719 } 720 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 721 "TRUTH paid until %s (%d)!\n", 722 GNUNET_TIME_relative2s ( 723 GNUNET_TIME_absolute_get_remaining ( 724 paid_until.abs_time), 725 GNUNET_YES), 726 qs); 727 } 728 else 729 { 730 paid_until 731 = GNUNET_TIME_relative_to_timestamp ( 732 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 733 ANASTASIS_MAX_YEARS_STORAGE)); 734 } 735 736 737 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 738 "Storing truth %s until %s!\n", 739 TALER_B2S (truth_uuid), 740 GNUNET_TIME_timestamp2s (paid_until)); 741 { 742 enum GNUNET_DB_QueryStatus qs; 743 744 qs = ANASTASIS_DB_insert_truth ( 745 truth_uuid, 746 &key_share_data, 747 (NULL == truth_mime) 748 ? "" 749 : truth_mime, 750 encrypted_truth, 751 encrypted_truth_size, 752 type, 753 GNUNET_TIME_absolute_get_remaining ( 754 paid_until.abs_time)); 755 switch (qs) 756 { 757 case GNUNET_DB_STATUS_HARD_ERROR: 758 case GNUNET_DB_STATUS_SOFT_ERROR: 759 GNUNET_break (0); 760 GNUNET_JSON_parse_free (spec); 761 return TALER_MHD_reply_with_error (connection, 762 MHD_HTTP_INTERNAL_SERVER_ERROR, 763 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 764 "insert_truth"); 765 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 766 { 767 void *xtruth; 768 size_t xtruth_size; 769 char *xtruth_mime; 770 char *xmethod; 771 bool ok = false; 772 773 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 774 ANASTASIS_DB_get_truth ( 775 truth_uuid, 776 &xtruth, 777 &xtruth_size, 778 &xtruth_mime, 779 &xmethod)) 780 { 781 ok = ( (xtruth_size == encrypted_truth_size) && 782 (0 == strcmp (xmethod, 783 type)) && 784 (0 == strcmp (((NULL == truth_mime) ? "" : truth_mime), 785 ((NULL == xtruth_mime) ? "" : xtruth_mime))) && 786 (0 == memcmp (xtruth, 787 encrypted_truth, 788 xtruth_size)) ); 789 /* NOTE: do NOT free encrypted_truth here; it is owned by `spec` 790 and released by GNUNET_JSON_parse_free (spec) below. Freeing it 791 here caused a double free on the idempotent fall-through. */ 792 GNUNET_free (xtruth); 793 GNUNET_free (xtruth_mime); 794 GNUNET_free (xmethod); 795 } 796 if (! ok) 797 { 798 GNUNET_JSON_parse_free (spec); 799 800 return TALER_MHD_reply_with_error (connection, 801 MHD_HTTP_CONFLICT, 802 TALER_EC_ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS, 803 NULL); 804 } 805 /* idempotency detected, intentional fall through! */ 806 } 807 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 808 { 809 struct MHD_Response *resp; 810 811 GNUNET_JSON_parse_free (spec); 812 resp = MHD_create_response_from_buffer (0, 813 NULL, 814 MHD_RESPMEM_PERSISTENT); 815 TALER_MHD_add_global_headers (resp, 816 false); 817 ret = MHD_queue_response (connection, 818 MHD_HTTP_NO_CONTENT, 819 resp); 820 MHD_destroy_response (resp); 821 GNUNET_break (MHD_YES == ret); 822 return ret; 823 } 824 } 825 } 826 GNUNET_JSON_parse_free (spec); 827 GNUNET_break (0); 828 return MHD_NO; 829 }