anastasis

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

anastasis-httpd_truth-upload.c (26606B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2019, 2021 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Anastasis; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file anastasis-httpd_truth_upload.c
     18  * @brief functions to handle incoming POST request on /truth
     19  * @author Dennis Neufeld
     20  * @author Dominik Meister
     21  * @author Christian Grothoff
     22  */
     23 #include "platform.h"
     24 #include "anastasis-httpd.h"
     25 #include "anastasis_service.h"
     26 #include "anastasis-httpd_truth.h"
     27 #include <gnunet/gnunet_util_lib.h>
     28 #include <gnunet/gnunet_rest_lib.h>
     29 #include <taler/taler_json_lib.h>
     30 #include <taler/taler_merchant_service.h>
     31 #include <taler/taler_signatures.h>
     32 #include "anastasis_authorization_lib.h"
     33 #include <taler/taler-merchant/post-private-orders.h>
     34 #include <taler/taler-merchant/get-private-orders-ORDER_ID.h>
     35 
     36 
     37 /**
     38  * Information we track per truth upload.
     39  */
     40 struct TruthUploadContext
     41 {
     42 
     43   /**
     44    * UUID of the truth object we are processing.
     45    */
     46   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
     47 
     48   /**
     49    * Kept in DLL for shutdown handling while suspended.
     50    */
     51   struct TruthUploadContext *next;
     52 
     53   /**
     54    * Kept in DLL for shutdown handling while suspended.
     55    */
     56   struct TruthUploadContext *prev;
     57 
     58   /**
     59    * Used while we are awaiting proposal creation.
     60    */
     61   struct TALER_MERCHANT_PostPrivateOrdersHandle *po;
     62 
     63   /**
     64    * Used while we are waiting payment.
     65    */
     66   struct TALER_MERCHANT_GetPrivateOrderHandle *cpo;
     67 
     68   /**
     69    * Post parser context.
     70    */
     71   void *post_ctx;
     72 
     73   /**
     74    * Handle to the client request.
     75    */
     76   struct MHD_Connection *connection;
     77 
     78   /**
     79    * Incoming JSON, NULL if not yet available.
     80    */
     81   json_t *json;
     82 
     83   /**
     84    * HTTP response code to use on resume, if non-NULL.
     85    */
     86   struct MHD_Response *resp;
     87 
     88   /**
     89    * When should this request time out?
     90    */
     91   struct GNUNET_TIME_Absolute timeout;
     92 
     93   /**
     94    * Fee that is to be paid for this upload.
     95    */
     96   struct TALER_Amount upload_fee;
     97 
     98   /**
     99    * HTTP response code to use on resume, if resp is set.
    100    */
    101   unsigned int response_code;
    102 
    103   /**
    104    * For how many years must the customer still pay?
    105    */
    106   unsigned int years_to_pay;
    107 
    108 };
    109 
    110 
    111 /**
    112  * Head of linked list over all truth upload processes
    113  */
    114 static struct TruthUploadContext *tuc_head;
    115 
    116 /**
    117  * Tail of linked list over all truth upload processes
    118  */
    119 static struct TruthUploadContext *tuc_tail;
    120 
    121 
    122 void
    123 AH_truth_upload_shutdown (void)
    124 {
    125   struct TruthUploadContext *tuc;
    126 
    127   while (NULL != (tuc = tuc_head))
    128   {
    129     GNUNET_CONTAINER_DLL_remove (tuc_head,
    130                                  tuc_tail,
    131                                  tuc);
    132     if (NULL != tuc->cpo)
    133     {
    134       TALER_MERCHANT_get_private_order_cancel (tuc->cpo);
    135       tuc->cpo = NULL;
    136     }
    137     if (NULL != tuc->po)
    138     {
    139       TALER_MERCHANT_post_private_orders_cancel (tuc->po);
    140       tuc->po = NULL;
    141     }
    142     MHD_resume_connection (tuc->connection);
    143   }
    144 }
    145 
    146 
    147 /**
    148  * Function called to clean up a `struct TruthUploadContext`.
    149  *
    150  * @param hc general handler context
    151  */
    152 static void
    153 cleanup_truth_post (struct TM_HandlerContext *hc)
    154 {
    155   struct TruthUploadContext *tuc = hc->ctx;
    156 
    157   TALER_MHD_parse_post_cleanup_callback (tuc->post_ctx);
    158   if (NULL != tuc->po)
    159     TALER_MERCHANT_post_private_orders_cancel (tuc->po);
    160   if (NULL != tuc->cpo)
    161     TALER_MERCHANT_get_private_order_cancel (tuc->cpo);
    162   if (NULL != tuc->resp)
    163     MHD_destroy_response (tuc->resp);
    164   if (NULL != tuc->json)
    165     json_decref (tuc->json);
    166   GNUNET_free (tuc);
    167 }
    168 
    169 
    170 /**
    171  * Transmit a payment request for @a tuc.
    172  *
    173  * @param tuc upload context to generate payment request for
    174  */
    175 static void
    176 make_payment_request (struct TruthUploadContext *tuc)
    177 {
    178   struct MHD_Response *resp;
    179 
    180   /* request payment via Taler */
    181   resp = MHD_create_response_from_buffer (0,
    182                                           NULL,
    183                                           MHD_RESPMEM_PERSISTENT);
    184   GNUNET_assert (NULL != resp);
    185   TALER_MHD_add_global_headers (resp,
    186                                 false);
    187   {
    188     char *hdr;
    189     const char *pfx;
    190     const char *hn;
    191 
    192     if (0 == strncasecmp ("https://",
    193                           AH_backend_url,
    194                           strlen ("https://")))
    195     {
    196       pfx = "taler://";
    197       hn = &AH_backend_url[strlen ("https://")];
    198     }
    199     else if (0 == strncasecmp ("http://",
    200                                AH_backend_url,
    201                                strlen ("http://")))
    202     {
    203       pfx = "taler+http://";
    204       hn = &AH_backend_url[strlen ("http://")];
    205     }
    206     else
    207     {
    208       /* This invariant holds as per check in anastasis-httpd.c */
    209       GNUNET_assert (0);
    210     }
    211     /* This invariant holds as per check in anastasis-httpd.c */
    212     GNUNET_assert (0 != strlen (hn));
    213     {
    214       char *order_id;
    215 
    216       order_id = GNUNET_STRINGS_data_to_string_alloc (
    217         &tuc->truth_uuid,
    218         sizeof (tuc->truth_uuid));
    219       GNUNET_asprintf (&hdr,
    220                        "%spay/%s%s/",
    221                        pfx,
    222                        hn,
    223                        order_id);
    224       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    225                   "Returning %u %s\n",
    226                   MHD_HTTP_PAYMENT_REQUIRED,
    227                   order_id);
    228       GNUNET_free (order_id);
    229     }
    230     GNUNET_break (MHD_YES ==
    231                   MHD_add_response_header (resp,
    232                                            ANASTASIS_HTTP_HEADER_TALER,
    233                                            hdr));
    234     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    235                 "TRUTH payment request made: %s\n",
    236                 hdr);
    237     GNUNET_free (hdr);
    238   }
    239   tuc->resp = resp;
    240   tuc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
    241 }
    242 
    243 
    244 /**
    245  * Callbacks of this type are used to serve the result of submitting a
    246  * POST /private/orders request to a merchant.
    247  *
    248  * @param cls our `struct TruthUploadContext`
    249  * @param por response details
    250  */
    251 static void
    252 proposal_cb (void *cls,
    253              const struct TALER_MERCHANT_PostPrivateOrdersResponse *por)
    254 {
    255   struct TruthUploadContext *tuc = cls;
    256 
    257   tuc->po = NULL;
    258   GNUNET_CONTAINER_DLL_remove (tuc_head,
    259                                tuc_tail,
    260                                tuc);
    261   MHD_resume_connection (tuc->connection);
    262   AH_trigger_daemon (NULL);
    263   if (MHD_HTTP_OK != por->hr.http_status)
    264   {
    265     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    266                 "Backend returned status %u/%d\n",
    267                 por->hr.http_status,
    268                 (int) por->hr.ec);
    269     GNUNET_break (0);
    270     tuc->resp = TALER_MHD_MAKE_JSON_PACK (
    271       GNUNET_JSON_pack_uint64 ("code",
    272                                TALER_EC_ANASTASIS_GENERIC_ORDER_CREATE_BACKEND_ERROR),
    273       GNUNET_JSON_pack_string ("hint",
    274                                "Failed to setup order with merchant backend"),
    275       GNUNET_JSON_pack_uint64 ("backend-ec",
    276                                por->hr.ec),
    277       GNUNET_JSON_pack_uint64 ("backend-http-status",
    278                                por->hr.http_status),
    279       GNUNET_JSON_pack_allow_null (
    280         GNUNET_JSON_pack_object_incref ("backend-reply",
    281                                         (json_t *) por->hr.reply)));
    282     tuc->response_code = MHD_HTTP_BAD_GATEWAY;
    283     return;
    284   }
    285   make_payment_request (tuc);
    286 }
    287 
    288 
    289 /**
    290  * Callback to process a GET /check-payment request
    291  *
    292  * @param cls our `struct PolicyUploadContext`
    293  * @param osr order status
    294  */
    295 static void
    296 check_payment_cb (void *cls,
    297                   const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    298 {
    299   struct TruthUploadContext *tuc = cls;
    300   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
    301 
    302   tuc->cpo = NULL;
    303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    304               "Checking backend order status returned %u\n",
    305               hr->http_status);
    306   switch (hr->http_status)
    307   {
    308   case 0:
    309     /* Likely timeout, complain! */
    310     tuc->response_code = MHD_HTTP_GATEWAY_TIMEOUT;
    311     tuc->resp = TALER_MHD_make_error (
    312       TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT,
    313       NULL);
    314     break;
    315   case MHD_HTTP_OK:
    316     switch (osr->details.ok.status)
    317     {
    318     case TALER_MERCHANT_OSC_PAID:
    319       {
    320         enum GNUNET_DB_QueryStatus qs;
    321         unsigned int years;
    322         struct GNUNET_TIME_Relative paid_until;
    323         const json_t *contract;
    324         struct TALER_Amount amount;
    325         struct GNUNET_JSON_Specification cspec[] = {
    326           TALER_JSON_spec_amount_any ("amount",
    327                                       &amount),
    328           GNUNET_JSON_spec_end ()
    329         };
    330 
    331         contract = osr->details.ok.details.paid.contract_terms;
    332         if (GNUNET_OK !=
    333             GNUNET_JSON_parse (contract,
    334                                cspec,
    335                                NULL, NULL))
    336         {
    337           GNUNET_break (0);
    338           tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    339           tuc->resp = TALER_MHD_make_error (
    340             TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    341             "contract terms in database are malformed");
    342           break;
    343         }
    344         years = TALER_amount_divide2 (&amount,
    345                                       &AH_truth_upload_fee);
    346         paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    347                                                     years);
    348         /* add 1 week grace period, otherwise if a user
    349            wants to pay for 1 year, the first seconds
    350            would have passed between making the payment
    351            and our subsequent check if +1 year was
    352            paid... So we actually say 1 year = 52 weeks
    353            on the server, while the client calculates
    354            with 365 days. */
    355         paid_until = GNUNET_TIME_relative_add (paid_until,
    356                                                GNUNET_TIME_UNIT_WEEKS);
    357         qs = db->record_truth_upload_payment (
    358           db->cls,
    359           &tuc->truth_uuid,
    360           &osr->details.ok.details.paid.deposit_total,
    361           paid_until);
    362         if (qs <= 0)
    363         {
    364           GNUNET_break (0);
    365           tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    366           tuc->resp = TALER_MHD_make_error (
    367             TALER_EC_GENERIC_DB_STORE_FAILED,
    368             "record_truth_upload_payment");
    369           break;
    370         }
    371       }
    372       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    373                   "Payment confirmed, resuming upload\n");
    374       break;
    375     case TALER_MERCHANT_OSC_UNPAID:
    376     case TALER_MERCHANT_OSC_CLAIMED:
    377       make_payment_request (tuc);
    378       break;
    379     }
    380     break;
    381   case MHD_HTTP_UNAUTHORIZED:
    382     /* Configuration issue, complain! */
    383     tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    384     tuc->resp = TALER_MHD_MAKE_JSON_PACK (
    385       GNUNET_JSON_pack_uint64 ("code",
    386                                TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED),
    387       GNUNET_JSON_pack_string ("hint",
    388                                TALER_ErrorCode_get_hint (
    389                                  TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED)),
    390       GNUNET_JSON_pack_uint64 ("backend-ec",
    391                                hr->ec),
    392       GNUNET_JSON_pack_uint64 ("backend-http-status",
    393                                hr->http_status),
    394       GNUNET_JSON_pack_allow_null (
    395         GNUNET_JSON_pack_object_incref ("backend-reply",
    396                                         (json_t *) hr->reply)));
    397     GNUNET_assert (NULL != tuc->resp);
    398     break;
    399   case MHD_HTTP_NOT_FOUND:
    400     /* Setup fresh order */
    401     {
    402       char *order_id;
    403       json_t *order;
    404 
    405       order_id = GNUNET_STRINGS_data_to_string_alloc (
    406         &tuc->truth_uuid,
    407         sizeof(tuc->truth_uuid));
    408       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    409                   "%u, setting up fresh order %s\n",
    410                   MHD_HTTP_NOT_FOUND,
    411                   order_id);
    412       order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s}",
    413                          "amount",
    414                          TALER_JSON_from_amount (&tuc->upload_fee),
    415                          "summary",
    416                          "Anastasis challenge storage fee",
    417                          "products",
    418                          "description", "challenge storage fee",
    419                          "quantity", (json_int_t) tuc->years_to_pay,
    420                          "unit", "years",
    421                          "order_id",
    422                          order_id);
    423       GNUNET_free (order_id);
    424       tuc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx,
    425                                                            AH_backend_url,
    426                                                            order);
    427       GNUNET_assert (NULL != tuc->po);
    428       GNUNET_assert (
    429         GNUNET_OK ==
    430         TALER_MERCHANT_post_private_orders_set_options (
    431           tuc->po,
    432           TALER_MERCHANT_post_private_orders_option_create_token (false)));
    433       GNUNET_assert (TALER_EC_NONE ==
    434                      TALER_MERCHANT_post_private_orders_start (tuc->po,
    435                                                                &proposal_cb,
    436                                                                tuc));
    437       AH_trigger_curl ();
    438       json_decref (order);
    439       return;
    440     }
    441   default:
    442     /* Unexpected backend response */
    443     tuc->response_code = MHD_HTTP_BAD_GATEWAY;
    444     tuc->resp = TALER_MHD_MAKE_JSON_PACK (
    445       GNUNET_JSON_pack_uint64 ("code",
    446                                TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR),
    447       GNUNET_JSON_pack_string ("hint",
    448                                TALER_ErrorCode_get_hint (
    449                                  TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR)),
    450       GNUNET_JSON_pack_uint64 ("backend-ec",
    451                                (json_int_t) hr->ec),
    452       GNUNET_JSON_pack_uint64 ("backend-http-status",
    453                                (json_int_t) hr->http_status),
    454       GNUNET_JSON_pack_allow_null (
    455         GNUNET_JSON_pack_object_incref ("backend-reply",
    456                                         (json_t *) hr->reply)));
    457     break;
    458   }
    459   GNUNET_CONTAINER_DLL_remove (tuc_head,
    460                                tuc_tail,
    461                                tuc);
    462   MHD_resume_connection (tuc->connection);
    463   AH_trigger_daemon (NULL);
    464 }
    465 
    466 
    467 /**
    468  * Helper function used to ask our backend to begin processing a
    469  * payment for the truth upload.  May perform asynchronous operations
    470  * by suspending the connection if required.
    471  *
    472  * @param tuc context to begin payment for.
    473  * @return MHD status code
    474  */
    475 static MHD_RESULT
    476 begin_payment (struct TruthUploadContext *tuc)
    477 {
    478   char *order_id;
    479   struct GNUNET_TIME_Relative timeout;
    480 
    481   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    482               "Checking backend order status...\n");
    483   timeout = GNUNET_TIME_absolute_get_remaining (tuc->timeout);
    484   order_id = GNUNET_STRINGS_data_to_string_alloc (
    485     &tuc->truth_uuid,
    486     sizeof (tuc->truth_uuid));
    487   tuc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx,
    488                                                       AH_backend_url,
    489                                                       order_id);
    490   GNUNET_free (order_id);
    491   if (NULL == tuc->cpo)
    492   {
    493     GNUNET_break (0);
    494     return TALER_MHD_reply_with_error (tuc->connection,
    495                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    496                                        TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED,
    497                                        "Could not check order status");
    498   }
    499   GNUNET_assert (GNUNET_OK ==
    500                  TALER_MERCHANT_get_private_order_set_options (
    501                    tuc->cpo,
    502                    TALER_MERCHANT_get_private_order_option_timeout (timeout)));
    503   if (TALER_EC_NONE !=
    504       TALER_MERCHANT_get_private_order_start (tuc->cpo,
    505                                               &check_payment_cb,
    506                                               tuc))
    507   {
    508     GNUNET_break (0);
    509     return TALER_MHD_reply_with_error (tuc->connection,
    510                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    511                                        TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED,
    512                                        "Could not check order status");
    513   }
    514   GNUNET_CONTAINER_DLL_insert (tuc_head,
    515                                tuc_tail,
    516                                tuc);
    517   MHD_suspend_connection (tuc->connection);
    518   return MHD_YES;
    519 }
    520 
    521 
    522 MHD_RESULT
    523 AH_handler_truth_post (
    524   struct MHD_Connection *connection,
    525   struct TM_HandlerContext *hc,
    526   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
    527   const char *truth_data,
    528   size_t *truth_data_size)
    529 {
    530   struct TruthUploadContext *tuc = hc->ctx;
    531   MHD_RESULT ret;
    532   int res;
    533   struct ANASTASIS_CRYPTO_EncryptedKeyShareP key_share_data;
    534   void *encrypted_truth;
    535   size_t encrypted_truth_size;
    536   const char *truth_mime = NULL;
    537   const char *type;
    538   uint32_t storage_years;
    539   struct GNUNET_TIME_Timestamp paid_until
    540     = GNUNET_TIME_UNIT_ZERO_TS;
    541   struct GNUNET_JSON_Specification spec[] = {
    542     GNUNET_JSON_spec_fixed_auto ("key_share_data",
    543                                  &key_share_data),
    544     GNUNET_JSON_spec_string ("type",
    545                              &type),
    546     GNUNET_JSON_spec_varsize ("encrypted_truth",
    547                               &encrypted_truth,
    548                               &encrypted_truth_size),
    549     GNUNET_JSON_spec_mark_optional (
    550       GNUNET_JSON_spec_string ("truth_mime",
    551                                &truth_mime),
    552       NULL),
    553     GNUNET_JSON_spec_uint32 ("storage_duration_years",
    554                              &storage_years),
    555     GNUNET_JSON_spec_end ()
    556   };
    557 
    558   if (NULL == tuc)
    559   {
    560     tuc = GNUNET_new (struct TruthUploadContext);
    561     tuc->connection = connection;
    562     tuc->truth_uuid = *truth_uuid;
    563     hc->ctx = tuc;
    564     hc->cc = &cleanup_truth_post;
    565     TALER_MHD_check_content_length (connection,
    566                                     AH_upload_limit_mb * 1024LLU * 1024LLU);
    567     tuc->timeout = GNUNET_TIME_relative_to_absolute (
    568       GNUNET_TIME_UNIT_SECONDS);
    569     TALER_MHD_parse_request_timeout (connection,
    570                                      &tuc->timeout);
    571   } /* end 'if (NULL == tuc)' */
    572 
    573   if (NULL != tuc->resp)
    574   {
    575     /* We generated a response asynchronously, queue that */
    576     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    577                 "Returning asynchronously generated response with HTTP status %u\n",
    578                 tuc->response_code);
    579     ret = MHD_queue_response (connection,
    580                               tuc->response_code,
    581                               tuc->resp);
    582     GNUNET_break (MHD_YES == ret);
    583     MHD_destroy_response (tuc->resp);
    584     tuc->resp = NULL;
    585     return ret;
    586   }
    587 
    588   if (NULL == tuc->json)
    589   {
    590     res = TALER_MHD_parse_post_json (connection,
    591                                      &tuc->post_ctx,
    592                                      truth_data,
    593                                      truth_data_size,
    594                                      &tuc->json);
    595     if (GNUNET_SYSERR == res)
    596     {
    597       GNUNET_break (0);
    598       return MHD_NO;
    599     }
    600     if ( (GNUNET_NO == res) ||
    601          (NULL == tuc->json) )
    602       return MHD_YES;
    603   }
    604   res = TALER_MHD_parse_json_data (connection,
    605                                    tuc->json,
    606                                    spec);
    607   if (GNUNET_SYSERR == res)
    608   {
    609     GNUNET_break (0);
    610     return MHD_NO;   /* hard failure */
    611   }
    612   if (GNUNET_NO == res)
    613   {
    614     GNUNET_break_op (0);
    615     return MHD_YES;   /* failure */
    616   }
    617 
    618   /* check method is supported */
    619   if ( (0 != strcmp ("question",
    620                      type)) &&
    621        (NULL ==
    622         ANASTASIS_authorization_plugin_load (type,
    623                                              db,
    624                                              AH_cfg)) )
    625   {
    626     GNUNET_JSON_parse_free (spec);
    627     return TALER_MHD_reply_with_error (connection,
    628                                        MHD_HTTP_BAD_REQUEST,
    629                                        TALER_EC_ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED,
    630                                        type);
    631   }
    632 
    633   if (storage_years > ANASTASIS_MAX_YEARS_STORAGE)
    634   {
    635     GNUNET_break_op (0);
    636     return TALER_MHD_reply_with_error (connection,
    637                                        MHD_HTTP_BAD_REQUEST,
    638                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    639                                        "storage_duration_years");
    640   }
    641   if (0 == storage_years)
    642     storage_years = 1;
    643 
    644   if (! TALER_amount_is_zero (&AH_truth_upload_fee))
    645   {
    646     struct GNUNET_TIME_Timestamp desired_until;
    647     enum GNUNET_DB_QueryStatus qs;
    648 
    649     desired_until
    650       = GNUNET_TIME_relative_to_timestamp (
    651           GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    652                                          storage_years));
    653     qs = db->check_truth_upload_paid (db->cls,
    654                                       truth_uuid,
    655                                       &paid_until);
    656     if (qs < 0)
    657       return TALER_MHD_reply_with_error (connection,
    658                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    659                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    660                                          NULL);
    661     if ( (0 == qs) ||
    662          (GNUNET_TIME_timestamp_cmp (paid_until,
    663                                      <,
    664                                      desired_until) ) )
    665     {
    666       struct GNUNET_TIME_Relative rem;
    667 
    668       if (GNUNET_TIME_absolute_is_past (paid_until.abs_time))
    669         paid_until = GNUNET_TIME_timestamp_get ();
    670       rem = GNUNET_TIME_absolute_get_difference (paid_until.abs_time,
    671                                                  desired_until.abs_time);
    672       tuc->years_to_pay = rem.rel_value_us
    673                           / GNUNET_TIME_UNIT_YEARS.rel_value_us;
    674       if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us))
    675         tuc->years_to_pay++;
    676       if (0 >
    677           TALER_amount_multiply (&tuc->upload_fee,
    678                                  &AH_truth_upload_fee,
    679                                  tuc->years_to_pay))
    680       {
    681         GNUNET_break_op (0);
    682         return TALER_MHD_reply_with_error (connection,
    683                                            MHD_HTTP_BAD_REQUEST,
    684                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
    685                                            "storage_duration_years");
    686       }
    687       if (! TALER_amount_is_zero (&tuc->upload_fee))
    688       {
    689         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    690                     "Truth upload payment required (%d)!\n",
    691                     qs);
    692         return begin_payment (tuc);
    693       }
    694     }
    695     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    696                 "TRUTH paid until %s (%d)!\n",
    697                 GNUNET_TIME_relative2s (
    698                   GNUNET_TIME_absolute_get_remaining (
    699                     paid_until.abs_time),
    700                   GNUNET_YES),
    701                 qs);
    702   }
    703   else
    704   {
    705     paid_until
    706       = GNUNET_TIME_relative_to_timestamp (
    707           GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    708                                          ANASTASIS_MAX_YEARS_STORAGE));
    709   }
    710 
    711 
    712   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    713               "Storing truth %s until %s!\n",
    714               TALER_B2S (truth_uuid),
    715               GNUNET_TIME_timestamp2s (paid_until));
    716   {
    717     enum GNUNET_DB_QueryStatus qs;
    718 
    719     qs = db->store_truth (db->cls,
    720                           truth_uuid,
    721                           &key_share_data,
    722                           (NULL == truth_mime)
    723                         ? ""
    724                         : truth_mime,
    725                           encrypted_truth,
    726                           encrypted_truth_size,
    727                           type,
    728                           GNUNET_TIME_absolute_get_remaining (
    729                             paid_until.abs_time));
    730     switch (qs)
    731     {
    732     case GNUNET_DB_STATUS_HARD_ERROR:
    733     case GNUNET_DB_STATUS_SOFT_ERROR:
    734       GNUNET_break (0);
    735       GNUNET_JSON_parse_free (spec);
    736       return TALER_MHD_reply_with_error (connection,
    737                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    738                                          TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    739                                          "store_truth");
    740     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    741       {
    742         void *xtruth;
    743         size_t xtruth_size;
    744         char *xtruth_mime;
    745         char *xmethod;
    746         bool ok = false;
    747 
    748         if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    749             db->get_escrow_challenge (db->cls,
    750                                       truth_uuid,
    751                                       &xtruth,
    752                                       &xtruth_size,
    753                                       &xtruth_mime,
    754                                       &xmethod))
    755         {
    756           ok = ( (xtruth_size == encrypted_truth_size) &&
    757                  (0 == strcmp (xmethod,
    758                                type)) &&
    759                  (0 == strcmp (((NULL == truth_mime) ? "" : truth_mime),
    760                                ((NULL == xtruth_mime) ? "" : xtruth_mime))) &&
    761                  (0 == memcmp (xtruth,
    762                                encrypted_truth,
    763                                xtruth_size)) );
    764           GNUNET_free (encrypted_truth);
    765           GNUNET_free (xtruth_mime);
    766           GNUNET_free (xmethod);
    767         }
    768         if (! ok)
    769         {
    770           GNUNET_JSON_parse_free (spec);
    771 
    772           return TALER_MHD_reply_with_error (connection,
    773                                              MHD_HTTP_CONFLICT,
    774                                              TALER_EC_ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS,
    775                                              NULL);
    776         }
    777         /* idempotency detected, intentional fall through! */
    778       }
    779     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    780       {
    781         struct MHD_Response *resp;
    782 
    783         GNUNET_JSON_parse_free (spec);
    784         resp = MHD_create_response_from_buffer (0,
    785                                                 NULL,
    786                                                 MHD_RESPMEM_PERSISTENT);
    787         TALER_MHD_add_global_headers (resp,
    788                                       false);
    789         ret = MHD_queue_response (connection,
    790                                   MHD_HTTP_NO_CONTENT,
    791                                   resp);
    792         MHD_destroy_response (resp);
    793         GNUNET_break (MHD_YES == ret);
    794         return ret;
    795       }
    796     }
    797   }
    798   GNUNET_JSON_parse_free (spec);
    799   GNUNET_break (0);
    800   return MHD_NO;
    801 }