anastasis

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

anastasis-httpd_truth-upload.c (26135B)


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