anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

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 }