anastasis

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

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 }