anastasis

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

anastasis-httpd_truth-upload.c (26784B)


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