anastasis

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

anastasis-httpd_truth-upload.c (26384B)


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