anastasis

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

anastasis-httpd_policy-upload.c (40861B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 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_policy.c
     18  * @brief functions to handle incoming requests on /policy/
     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-httpd_policy.h"
     26 #include "anastasis_service.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 <taler/taler-merchant/post-private-orders.h>
     33 #include <taler/taler-merchant/get-private-orders-ORDER_ID.h>
     34 
     35 /**
     36  * How long do we hold an HTTP client connection if
     37  * we are awaiting payment before giving up?
     38  */
     39 #define CHECK_PAYMENT_GENERIC_TIMEOUT GNUNET_TIME_relative_multiply ( \
     40           GNUNET_TIME_UNIT_SECONDS, 30)
     41 
     42 
     43 /**
     44  * Context for an upload operation.
     45  */
     46 struct PolicyUploadContext
     47 {
     48 
     49   /**
     50    * Signature of the account holder.
     51    */
     52   struct ANASTASIS_AccountSignatureP account_sig;
     53 
     54   /**
     55    * Public key of the account holder.
     56    */
     57   struct ANASTASIS_CRYPTO_AccountPublicKeyP account;
     58 
     59   /**
     60    * Hash of the upload we are receiving right now (as promised
     61    * by the client, to be verified!).
     62    */
     63   struct GNUNET_HashCode new_policy_upload_hash;
     64 
     65   /**
     66    * Hash context for the upload.
     67    */
     68   struct GNUNET_HashContext *hash_ctx;
     69 
     70   /**
     71    * Kept in DLL for shutdown handling while suspended.
     72    */
     73   struct PolicyUploadContext *next;
     74 
     75   /**
     76    * Kept in DLL for shutdown handling while suspended.
     77    */
     78   struct PolicyUploadContext *prev;
     79 
     80   /**
     81    * Used while suspended for resumption.
     82    */
     83   struct MHD_Connection *con;
     84 
     85   /**
     86    * Upload, with as many bytes as we have received so far.
     87    */
     88   char *upload;
     89 
     90   /**
     91    * Meta data uploaded by the client, or NULL for none.
     92    */
     93   void *meta_data;
     94 
     95   /**
     96    * Number of bytes in @e meta_data.
     97    */
     98   size_t meta_data_size;
     99 
    100   /**
    101    * Used while we are awaiting proposal creation.
    102    */
    103   struct TALER_MERCHANT_PostPrivateOrdersHandle *po;
    104 
    105   /**
    106    * Used while we are waiting payment.
    107    */
    108   struct TALER_MERCHANT_GetPrivateOrderHandle *cpo;
    109 
    110   /**
    111    * HTTP response code to use on resume, if non-NULL.
    112    */
    113   struct MHD_Response *resp;
    114 
    115   /**
    116    * Order under which the client promised payment, or NULL.
    117    */
    118   const char *order_id;
    119 
    120   /**
    121    * Payment Identifier
    122    */
    123   struct ANASTASIS_PaymentSecretP payment_identifier;
    124 
    125   /**
    126    * Timestamp of the order in @e payment_identifier. Used to
    127    * select the most recent unpaid offer.
    128    */
    129   struct GNUNET_TIME_Timestamp existing_pi_timestamp;
    130 
    131   /**
    132    * When does the operation timeout?
    133    */
    134   struct GNUNET_TIME_Absolute timeout;
    135 
    136   /**
    137    * How long must the account be valid?  Determines whether we should
    138    * trigger payment, and if so how much.
    139    */
    140   struct GNUNET_TIME_Timestamp end_date;
    141 
    142   /**
    143    * How long is the account already valid?
    144    * Determines how much the user needs to pay.
    145    */
    146   struct GNUNET_TIME_Timestamp paid_until;
    147 
    148   /**
    149    * Expected total upload size.
    150    */
    151   size_t upload_size;
    152 
    153   /**
    154    * Current offset for the upload.
    155    */
    156   size_t upload_off;
    157 
    158   /**
    159    * HTTP response code to use on resume, if resp is set.
    160    */
    161   unsigned int response_code;
    162 
    163   /**
    164    * For how many years does the client still have
    165    * to pay?
    166    */
    167   unsigned int years_to_pay;
    168 
    169   /**
    170    * true if client provided a payment secret / order ID?
    171    */
    172   bool payment_identifier_provided;
    173 
    174 };
    175 
    176 
    177 /**
    178  * Kept in DLL for shutdown handling while suspended.
    179  */
    180 static struct PolicyUploadContext *puc_head;
    181 
    182 /**
    183  * Kept in DLL for shutdown handling while suspended.
    184  */
    185 static struct PolicyUploadContext *puc_tail;
    186 
    187 
    188 /**
    189  * Service is shutting down, resume all MHD connections NOW.
    190  */
    191 void
    192 AH_resume_all_bc ()
    193 {
    194   struct PolicyUploadContext *puc;
    195 
    196   while (NULL != (puc = puc_head))
    197   {
    198     GNUNET_CONTAINER_DLL_remove (puc_head,
    199                                  puc_tail,
    200                                  puc);
    201     if (NULL != puc->po)
    202     {
    203       TALER_MERCHANT_post_private_orders_cancel (puc->po);
    204       puc->po = NULL;
    205     }
    206     if (NULL != puc->cpo)
    207     {
    208       TALER_MERCHANT_get_private_order_cancel (puc->cpo);
    209       puc->cpo = NULL;
    210     }
    211     MHD_resume_connection (puc->con);
    212   }
    213 }
    214 
    215 
    216 /**
    217  * Function called to clean up a backup context.
    218  *
    219  * @param hc a `struct PolicyUploadContext`
    220  */
    221 static void
    222 cleanup_ctx (struct TM_HandlerContext *hc)
    223 {
    224   struct PolicyUploadContext *puc = hc->ctx;
    225 
    226   if (NULL != puc->po)
    227     TALER_MERCHANT_post_private_orders_cancel (puc->po);
    228   if (NULL != puc->cpo)
    229     TALER_MERCHANT_get_private_order_cancel (puc->cpo);
    230   if (NULL != puc->hash_ctx)
    231     GNUNET_CRYPTO_hash_context_abort (puc->hash_ctx);
    232   if (NULL != puc->resp)
    233     MHD_destroy_response (puc->resp);
    234   GNUNET_free (puc->upload);
    235   GNUNET_free (puc->meta_data);
    236   GNUNET_free (puc);
    237 }
    238 
    239 
    240 /**
    241  * Transmit a payment request for @a order_id on @a connection
    242  *
    243  * @param[in,out] puc details about the operation
    244  * @return #GNUNET_OK on success
    245  */
    246 static int
    247 make_payment_request (struct PolicyUploadContext *puc)
    248 {
    249   struct MHD_Response *resp;
    250 
    251   /* request payment via Taler */
    252   resp = MHD_create_response_from_buffer (0,
    253                                           NULL,
    254                                           MHD_RESPMEM_PERSISTENT);
    255   if (NULL == resp)
    256   {
    257     GNUNET_break (0);
    258     return GNUNET_SYSERR;
    259   }
    260   TALER_MHD_add_global_headers (resp,
    261                                 false);
    262   {
    263     char *hdr;
    264     const char *pfx;
    265     char *hn;
    266 
    267     if (0 == strncasecmp ("https://",
    268                           AH_backend_url,
    269                           strlen ("https://")))
    270     {
    271       pfx = "taler://";
    272       hn = &AH_backend_url[strlen ("https://")];
    273     }
    274     else if (0 == strncasecmp ("http://",
    275                                AH_backend_url,
    276                                strlen ("http://")))
    277     {
    278       pfx = "taler+http://";
    279       hn = &AH_backend_url[strlen ("http://")];
    280     }
    281     else
    282     {
    283       GNUNET_break (0);
    284       MHD_destroy_response (resp);
    285       return GNUNET_SYSERR;
    286     }
    287     if (0 == strlen (hn))
    288     {
    289       GNUNET_break (0);
    290       MHD_destroy_response (resp);
    291       return GNUNET_SYSERR;
    292     }
    293     {
    294       char *order_id;
    295 
    296       order_id = GNUNET_STRINGS_data_to_string_alloc (
    297         &puc->payment_identifier,
    298         sizeof (puc->payment_identifier));
    299       GNUNET_asprintf (&hdr,
    300                        "%spay/%s%s/",
    301                        pfx,
    302                        hn,
    303                        order_id);
    304       GNUNET_free (order_id);
    305     }
    306     GNUNET_break (MHD_YES ==
    307                   MHD_add_response_header (resp,
    308                                            ANASTASIS_HTTP_HEADER_TALER,
    309                                            hdr));
    310     GNUNET_free (hdr);
    311   }
    312   puc->resp = resp;
    313   puc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
    314   return GNUNET_OK;
    315 }
    316 
    317 
    318 /**
    319  * Callbacks of this type are used to serve the result of submitting a
    320  * POST /private/orders request to a merchant.
    321  *
    322  * @param cls our `struct PolicyUploadContext`
    323  * @param por response details
    324  */
    325 static void
    326 proposal_cb (void *cls,
    327              const struct TALER_MERCHANT_PostPrivateOrdersResponse *por)
    328 {
    329   struct PolicyUploadContext *puc = cls;
    330   enum GNUNET_DB_QueryStatus qs;
    331 
    332   puc->po = NULL;
    333   GNUNET_CONTAINER_DLL_remove (puc_head,
    334                                puc_tail,
    335                                puc);
    336   MHD_resume_connection (puc->con);
    337   AH_trigger_daemon (NULL);
    338   if (MHD_HTTP_OK != por->hr.http_status)
    339   {
    340     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    341                 "Backend returned status %u/%d when trying to setup order\n",
    342                 por->hr.http_status,
    343                 (int) por->hr.ec);
    344     puc->resp = TALER_MHD_MAKE_JSON_PACK (
    345       GNUNET_JSON_pack_uint64 ("code",
    346                                TALER_EC_SYNC_PAYMENT_CREATE_BACKEND_ERROR),
    347       GNUNET_JSON_pack_string ("hint",
    348                                "Failed to setup order with merchant backend"),
    349       GNUNET_JSON_pack_uint64 ("backend-ec",
    350                                por->hr.ec),
    351       GNUNET_JSON_pack_uint64 ("backend-http-status",
    352                                por->hr.http_status),
    353       GNUNET_JSON_pack_allow_null (
    354         GNUNET_JSON_pack_object_incref ("backend-reply",
    355                                         (json_t *) por->hr.reply)));
    356     puc->response_code = MHD_HTTP_BAD_GATEWAY;
    357     return;
    358   }
    359   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    360               "Storing payment request for order `%s'\n",
    361               por->details.ok.order_id);
    362 
    363   qs = db->record_recdoc_payment (db->cls,
    364                                   &puc->account,
    365                                   (uint32_t) AH_post_counter,
    366                                   &puc->payment_identifier,
    367                                   &AH_annual_fee);
    368   if (0 >= qs)
    369   {
    370     GNUNET_break (0);
    371     puc->resp = TALER_MHD_make_error (
    372       TALER_EC_GENERIC_DB_STORE_FAILED,
    373       "record recdoc payment");
    374     puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    375     return;
    376   }
    377   if (GNUNET_OK !=
    378       make_payment_request (puc))
    379   {
    380     GNUNET_break (0);
    381     puc->resp = TALER_MHD_make_error (
    382       TALER_EC_GENERIC_DB_STORE_FAILED,
    383       "failed to initiate payment");
    384     puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    385   }
    386 }
    387 
    388 
    389 /**
    390  * Callback to process a GET /check-payment request
    391  *
    392  * @param cls our `struct PolicyUploadContext`
    393  * @param osr order status
    394  */
    395 static void
    396 check_payment_cb (void *cls,
    397                   const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    398 {
    399   struct PolicyUploadContext *puc = cls;
    400   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
    401 
    402   /* refunds are not supported, verify */
    403   puc->cpo = NULL;
    404   GNUNET_CONTAINER_DLL_remove (puc_head,
    405                                puc_tail,
    406                                puc);
    407   MHD_resume_connection (puc->con);
    408   AH_trigger_daemon (NULL);
    409   switch (hr->http_status)
    410   {
    411   case MHD_HTTP_OK:
    412     GNUNET_assert (NULL != osr);
    413     break; /* processed below */
    414   case MHD_HTTP_UNAUTHORIZED:
    415     puc->resp = TALER_MHD_make_error (
    416       TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED,
    417       NULL);
    418     puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    419     return;
    420   default:
    421     puc->resp = TALER_MHD_make_error (
    422       TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR,
    423       "failed to initiate payment");
    424     puc->response_code = MHD_HTTP_BAD_GATEWAY;
    425     return;
    426   }
    427 
    428   GNUNET_assert (MHD_HTTP_OK == hr->http_status);
    429   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    430               "Payment status checked: %d\n",
    431               osr->details.ok.status);
    432   switch (osr->details.ok.status)
    433   {
    434   case TALER_MERCHANT_OSC_PAID:
    435     {
    436       enum GNUNET_DB_QueryStatus qs;
    437       unsigned int years;
    438       struct GNUNET_TIME_Relative paid_until;
    439       const json_t *contract;
    440       struct TALER_Amount amount;
    441       struct GNUNET_JSON_Specification cspec[] = {
    442         TALER_JSON_spec_amount_any ("amount",
    443                                     &amount),
    444         GNUNET_JSON_spec_end ()
    445       };
    446 
    447       contract = osr->details.ok.details.paid.contract_terms;
    448       if (GNUNET_OK !=
    449           GNUNET_JSON_parse (contract,
    450                              cspec,
    451                              NULL, NULL))
    452       {
    453         GNUNET_break (0);
    454         puc->resp = TALER_MHD_make_error (
    455           TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    456           "no amount given");
    457         puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    458         return; /* continue as planned */
    459       }
    460       years = TALER_amount_divide2 (&amount,
    461                                     &AH_annual_fee);
    462       paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    463                                                   years);
    464       /* add 1 week grace period, otherwise if a user
    465          wants to pay for 1 year, the first seconds
    466          would have passed between making the payment
    467          and our subsequent check if +1 year was
    468          paid... So we actually say 1 year = 52 weeks
    469          on the server, while the client calculates
    470          with 365 days. */
    471       paid_until = GNUNET_TIME_relative_add (paid_until,
    472                                              GNUNET_TIME_UNIT_WEEKS);
    473 
    474       qs = db->increment_lifetime (db->cls,
    475                                    &puc->account,
    476                                    &puc->payment_identifier,
    477                                    paid_until,
    478                                    &puc->paid_until);
    479       if (0 <= qs)
    480         return; /* continue as planned */
    481       GNUNET_break (0);
    482       puc->resp = TALER_MHD_make_error (
    483         TALER_EC_GENERIC_DB_FETCH_FAILED,
    484         "increment lifetime");
    485       puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    486       return; /* continue as planned */
    487     }
    488   case TALER_MERCHANT_OSC_UNPAID:
    489   case TALER_MERCHANT_OSC_CLAIMED:
    490     break;
    491   }
    492   if (! GNUNET_TIME_absolute_is_zero (puc->existing_pi_timestamp.abs_time))
    493   {
    494     /* repeat payment request */
    495     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    496                 "Repeating payment request\n");
    497     if (GNUNET_OK !=
    498         make_payment_request (puc))
    499     {
    500       GNUNET_break (0);
    501       puc->resp = TALER_MHD_make_error (
    502         TALER_EC_GENERIC_DB_STORE_FAILED,
    503         "failed to initiate payment");
    504       puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    505     }
    506     return;
    507   }
    508   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    509               "Timeout waiting for payment\n");
    510   puc->resp = TALER_MHD_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT,
    511                                     "Timeout awaiting promised payment");
    512   GNUNET_assert (NULL != puc->resp);
    513   puc->response_code = MHD_HTTP_REQUEST_TIMEOUT;
    514 }
    515 
    516 
    517 /**
    518  * Helper function used to ask our backend to await
    519  * a payment for the user's account.
    520  *
    521  * @param puc context to begin payment for.
    522  */
    523 static void
    524 await_payment (struct PolicyUploadContext *puc)
    525 {
    526   struct GNUNET_TIME_Relative timeout
    527     = GNUNET_TIME_absolute_get_remaining (puc->timeout);
    528 
    529   GNUNET_CONTAINER_DLL_insert (puc_head,
    530                                puc_tail,
    531                                puc);
    532   MHD_suspend_connection (puc->con);
    533   {
    534     char *order_id;
    535 
    536     order_id = GNUNET_STRINGS_data_to_string_alloc (
    537       &puc->payment_identifier,
    538       sizeof(struct ANASTASIS_PaymentSecretP));
    539     puc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx,
    540                                                         AH_backend_url,
    541                                                         order_id);
    542     GNUNET_assert (NULL != puc->cpo);
    543     GNUNET_free (order_id);
    544     GNUNET_assert (
    545       GNUNET_OK ==
    546       TALER_MERCHANT_get_private_order_set_options (
    547         puc->cpo,
    548         TALER_MERCHANT_get_private_order_option_timeout (timeout)));
    549     GNUNET_assert (
    550       TALER_EC_NONE ==
    551       TALER_MERCHANT_get_private_order_start (puc->cpo,
    552                                               &check_payment_cb,
    553                                               puc));
    554   }
    555   AH_trigger_curl ();
    556 }
    557 
    558 
    559 /**
    560  * Helper function used to ask our backend to begin processing a
    561  * payment for the user's account.  May perform asynchronous
    562  * operations by suspending the connection if required.
    563  *
    564  * @param puc context to begin payment for.
    565  * @return MHD status code
    566  */
    567 static MHD_RESULT
    568 begin_payment (struct PolicyUploadContext *puc)
    569 {
    570   json_t *order;
    571 
    572   GNUNET_CONTAINER_DLL_insert (puc_head,
    573                                puc_tail,
    574                                puc);
    575   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    576               "Suspending connection while creating order at `%s'\n",
    577               AH_backend_url);
    578   {
    579     char *order_id;
    580     struct TALER_Amount upload_fee;
    581 
    582     if (0 >
    583         TALER_amount_multiply (&upload_fee,
    584                                &AH_annual_fee,
    585                                puc->years_to_pay))
    586     {
    587       GNUNET_break_op (0);
    588       return TALER_MHD_reply_with_error (puc->con,
    589                                          MHD_HTTP_BAD_REQUEST,
    590                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    591                                          "storage_duration_years");
    592     }
    593 
    594     order_id = GNUNET_STRINGS_data_to_string_alloc (
    595       &puc->payment_identifier,
    596       sizeof(struct ANASTASIS_PaymentSecretP));
    597     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    598                 "Creating order for %u years with payment of %s\n",
    599                 puc->years_to_pay,
    600                 TALER_amount2s (&upload_fee));
    601     order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s }",
    602                        "amount", TALER_JSON_from_amount (&upload_fee),
    603                        "summary", "Anastasis policy storage fee",
    604                        "products",
    605                        "description", "policy storage fee",
    606                        "quantity", (json_int_t) puc->years_to_pay,
    607                        "unit", "years",
    608                        "order_id", order_id);
    609     GNUNET_free (order_id);
    610   }
    611   MHD_suspend_connection (puc->con);
    612   puc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx,
    613                                                        AH_backend_url,
    614                                                        order);
    615   GNUNET_assert (NULL != puc->po);
    616   GNUNET_assert (
    617     GNUNET_OK ==
    618     TALER_MERCHANT_post_private_orders_set_options (
    619       puc->po,
    620       TALER_MERCHANT_post_private_orders_option_create_token (false)));
    621   GNUNET_assert (TALER_EC_NONE ==
    622                  TALER_MERCHANT_post_private_orders_start (puc->po,
    623                                                            &proposal_cb,
    624                                                            puc));
    625   AH_trigger_curl ();
    626   json_decref (order);
    627   return MHD_YES;
    628 }
    629 
    630 
    631 /**
    632  * Prepare to receive a payment, possibly requesting it, or just waiting
    633  * for it to be completed by the client.
    634  *
    635  * @param puc context to prepare payment for
    636  * @return MHD status
    637  */
    638 static MHD_RESULT
    639 prepare_payment (struct PolicyUploadContext *puc)
    640 {
    641   if (! puc->payment_identifier_provided)
    642   {
    643     GNUNET_CRYPTO_random_block (
    644       GNUNET_CRYPTO_QUALITY_NONCE,
    645       &puc->payment_identifier,
    646       sizeof (struct ANASTASIS_PaymentSecretP));
    647     puc->payment_identifier_provided = true;
    648     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    649                 "No payment identifier, initiating payment\n");
    650     return begin_payment (puc);
    651   }
    652   await_payment (puc);
    653   return MHD_YES;
    654 }
    655 
    656 
    657 MHD_RESULT
    658 AH_handler_policy_post (
    659   struct MHD_Connection *connection,
    660   struct TM_HandlerContext *hc,
    661   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub,
    662   const char *recovery_data,
    663   size_t *recovery_data_size)
    664 {
    665   struct PolicyUploadContext *puc = hc->ctx;
    666 
    667   if (NULL == puc)
    668   {
    669     /* first call, setup internals */
    670     puc = GNUNET_new (struct PolicyUploadContext);
    671     hc->ctx = puc;
    672     hc->cc = &cleanup_ctx;
    673     puc->con = connection;
    674 
    675     TALER_MHD_parse_request_header_auto (connection,
    676                                          ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER,
    677                                          &puc->payment_identifier,
    678                                          puc->payment_identifier_provided);
    679     puc->account = *account_pub;
    680 
    681     /* check for meta-data */
    682     {
    683       const char *metas;
    684 
    685       metas = MHD_lookup_connection_value (connection,
    686                                            MHD_HEADER_KIND,
    687                                            ANASTASIS_HTTP_HEADER_POLICY_META_DATA);
    688       if (NULL == metas)
    689       {
    690         GNUNET_break_op (0);
    691         return TALER_MHD_reply_with_error (
    692           connection,
    693           MHD_HTTP_BAD_REQUEST,
    694           TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    695           ANASTASIS_HTTP_HEADER_POLICY_META_DATA
    696           " header must be present");
    697       }
    698       if (GNUNET_OK !=
    699           GNUNET_STRINGS_string_to_data_alloc (metas,
    700                                                strlen (metas),
    701                                                &puc->meta_data,
    702                                                &puc->meta_data_size))
    703       {
    704         GNUNET_break_op (0);
    705         return TALER_MHD_reply_with_error (
    706           connection,
    707           MHD_HTTP_BAD_REQUEST,
    708           TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    709           ANASTASIS_HTTP_HEADER_POLICY_META_DATA
    710           " header must include a base32-encoded value");
    711       }
    712     }
    713     /* now setup 'puc' */
    714     {
    715       const char *lens;
    716       unsigned long len;
    717       char dummy;
    718 
    719       lens = MHD_lookup_connection_value (connection,
    720                                           MHD_HEADER_KIND,
    721                                           MHD_HTTP_HEADER_CONTENT_LENGTH);
    722       if ( (NULL == lens) ||
    723            (1 != sscanf (lens,
    724                          "%lu%c",
    725                          &len,
    726                          &dummy)) )
    727       {
    728         GNUNET_break_op (0);
    729         return TALER_MHD_reply_with_error (
    730           connection,
    731           MHD_HTTP_BAD_REQUEST,
    732           (NULL == lens)
    733           ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH
    734           : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH,
    735           NULL);
    736       }
    737       if (len / 1024 / 1024 >= AH_upload_limit_mb)
    738       {
    739         GNUNET_break_op (0);
    740         return TALER_MHD_reply_with_error (connection,
    741                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
    742                                            TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH,
    743                                            "Content-length value not acceptable");
    744       }
    745       puc->upload = GNUNET_malloc_large (len);
    746       if (NULL == puc->upload)
    747       {
    748         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    749                              "malloc");
    750         return TALER_MHD_reply_with_error (connection,
    751                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
    752                                            TALER_EC_ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH,
    753                                            NULL);
    754       }
    755       puc->upload_size = (size_t) len;
    756     }
    757 
    758     TALER_MHD_parse_request_header_auto_t (connection,
    759                                            ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE,
    760                                            &puc->account_sig);
    761     {
    762       /* Check if header contains an ETAG */
    763       const char *etag;
    764 
    765       etag = MHD_lookup_connection_value (connection,
    766                                           MHD_HEADER_KIND,
    767                                           MHD_HTTP_HEADER_IF_NONE_MATCH);
    768       if ( (NULL == etag) ||
    769            (2 >= strlen (etag)) ||
    770            ('"' != etag[0]) ||
    771            ('"' != etag[strlen (etag) - 1]) ||
    772            (GNUNET_OK !=
    773             GNUNET_STRINGS_string_to_data (etag + 1,
    774                                            strlen (etag) - 2,
    775                                            &puc->new_policy_upload_hash,
    776                                            sizeof (puc->new_policy_upload_hash))
    777            ) )
    778       {
    779         GNUNET_break_op (0);
    780         return TALER_MHD_reply_with_error (connection,
    781                                            MHD_HTTP_BAD_REQUEST,
    782                                            TALER_EC_ANASTASIS_POLICY_BAD_IF_MATCH,
    783                                            MHD_HTTP_HEADER_IF_NONE_MATCH
    784                                            " header must include a base32-encoded SHA-512 hash");
    785       }
    786     }
    787     /* validate signature */
    788     {
    789       struct ANASTASIS_UploadSignaturePS usp = {
    790         .purpose.size = htonl (sizeof (usp)),
    791         .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD),
    792         .new_recovery_data_hash = puc->new_policy_upload_hash
    793       };
    794 
    795       if (GNUNET_OK !=
    796           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD,
    797                                       &usp,
    798                                       &puc->account_sig.eddsa_sig,
    799                                       &account_pub->pub))
    800       {
    801         GNUNET_break_op (0);
    802         return TALER_MHD_reply_with_error (connection,
    803                                            MHD_HTTP_FORBIDDEN,
    804                                            TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE,
    805                                            ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE);
    806       }
    807     }
    808 
    809     puc->timeout = GNUNET_TIME_relative_to_absolute (
    810       CHECK_PAYMENT_GENERIC_TIMEOUT);
    811     TALER_MHD_parse_request_timeout (connection,
    812                                      &puc->timeout);
    813 
    814     /* check if the client insists on paying */
    815     {
    816       const char *req;
    817       unsigned int years;
    818 
    819       req = MHD_lookup_connection_value (connection,
    820                                          MHD_GET_ARGUMENT_KIND,
    821                                          "storage_duration");
    822       if (NULL != req)
    823       {
    824         char dummy;
    825 
    826         if (1 != sscanf (req,
    827                          "%u%c",
    828                          &years,
    829                          &dummy))
    830         {
    831           GNUNET_break_op (0);
    832           return TALER_MHD_reply_with_error (connection,
    833                                              MHD_HTTP_BAD_REQUEST,
    834                                              TALER_EC_GENERIC_PARAMETER_MALFORMED,
    835                                              "storage_duration (must be non-negative number)");
    836         }
    837       }
    838       else
    839       {
    840         years = 1;
    841       }
    842       puc->end_date = GNUNET_TIME_relative_to_timestamp (
    843         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    844                                        years));
    845     }
    846 
    847     /* get ready to hash (done here as we may go async for payments next) */
    848     puc->hash_ctx = GNUNET_CRYPTO_hash_context_start ();
    849 
    850     /* Check database to see if the transaction is permissible */
    851     {
    852       struct GNUNET_TIME_Relative rem;
    853 
    854       rem = GNUNET_TIME_absolute_get_remaining (puc->end_date.abs_time);
    855       puc->years_to_pay = rem.rel_value_us
    856                           / GNUNET_TIME_UNIT_YEARS.rel_value_us;
    857       if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us))
    858         puc->years_to_pay++;
    859       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    860                   "Calculated years to pay to be %u until %s\n",
    861                   puc->years_to_pay,
    862                   GNUNET_TIME_absolute2s (puc->end_date.abs_time));
    863 
    864       if (puc->payment_identifier_provided)
    865       {
    866         /* check if payment identifier is valid (existing and paid) */
    867         bool paid;
    868         bool valid_counter;
    869         enum GNUNET_DB_QueryStatus qs;
    870 
    871         qs = db->check_payment_identifier (db->cls,
    872                                            &puc->payment_identifier,
    873                                            &paid,
    874                                            &valid_counter);
    875         if (qs < 0)
    876           return TALER_MHD_reply_with_error (puc->con,
    877                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    878                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    879                                              NULL);
    880 
    881         if ( (! paid) ||
    882              (! valid_counter) )
    883         {
    884           if (! valid_counter)
    885           {
    886             puc->payment_identifier_provided = false;
    887             if (0 == puc->years_to_pay)
    888               puc->years_to_pay = 1;
    889             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    890                         "Too many uploads with this payment identifier, initiating fresh payment\n");
    891           }
    892           else
    893           {
    894             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    895                         "Given payment identifier not known to be paid, initiating payment\n");
    896           }
    897           return prepare_payment (puc);
    898         }
    899       }
    900 
    901       if (! puc->payment_identifier_provided)
    902       {
    903         enum GNUNET_DB_QueryStatus qs;
    904         struct GNUNET_TIME_Relative rel;
    905 
    906         /* generate fresh payment identifier */
    907         GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
    908                                     &puc->payment_identifier,
    909                                     sizeof (struct ANASTASIS_PaymentSecretP));
    910         if (! TALER_amount_is_zero (&AH_annual_fee))
    911         {
    912           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    913                       "No payment identifier, requesting payment\n");
    914           return begin_payment (puc);
    915         }
    916         /* Cost is zero, fake "zero" payment having happened */
    917         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    918                     "Policy upload is free, allowing upload without payment\n");
    919         qs = db->record_recdoc_payment (db->cls,
    920                                         account_pub,
    921                                         AH_post_counter,
    922                                         &puc->payment_identifier,
    923                                         &AH_annual_fee);
    924         if (qs <= 0)
    925           return TALER_MHD_reply_with_error (puc->con,
    926                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    927                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    928                                              NULL);
    929         rel = GNUNET_TIME_relative_multiply (
    930           GNUNET_TIME_UNIT_YEARS,
    931           ANASTASIS_MAX_YEARS_STORAGE);
    932         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    933                     "Policy lifetime is %s (%u years)\n",
    934                     GNUNET_TIME_relative2s (rel,
    935                                             true),
    936                     ANASTASIS_MAX_YEARS_STORAGE);
    937         puc->paid_until = GNUNET_TIME_relative_to_timestamp (rel);
    938         qs = db->update_lifetime (db->cls,
    939                                   account_pub,
    940                                   &puc->payment_identifier,
    941                                   puc->paid_until);
    942         if (qs <= 0)
    943         {
    944           GNUNET_break (0);
    945           return TALER_MHD_reply_with_error (puc->con,
    946                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    947                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    948                                              NULL);
    949         }
    950       }
    951     }
    952 
    953     /* Check if existing policy matches upload (and if, skip it) */
    954     {
    955       struct GNUNET_HashCode hash;
    956       enum ANASTASIS_DB_AccountStatus as;
    957       uint32_t version;
    958       struct GNUNET_TIME_Timestamp now;
    959       struct GNUNET_TIME_Relative rem;
    960 
    961       as = db->lookup_account (db->cls,
    962                                account_pub,
    963                                &puc->paid_until,
    964                                &hash,
    965                                &version);
    966       now = GNUNET_TIME_timestamp_get ();
    967       if (GNUNET_TIME_timestamp_cmp (puc->paid_until,
    968                                      <,
    969                                      now))
    970         puc->paid_until = now;
    971       rem = GNUNET_TIME_absolute_get_difference (puc->paid_until.abs_time,
    972                                                  puc->end_date.abs_time);
    973       puc->years_to_pay = rem.rel_value_us
    974                           / GNUNET_TIME_UNIT_YEARS.rel_value_us;
    975       if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us))
    976         puc->years_to_pay++;
    977       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    978                   "Calculated years to pay to be %u until %s\n",
    979                   puc->years_to_pay,
    980                   GNUNET_TIME_absolute2s (puc->end_date.abs_time));
    981       if ( (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED == as) &&
    982            (0 != puc->years_to_pay) )
    983       {
    984         /* user requested extension, force payment */
    985         as = ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED;
    986       }
    987       switch (as)
    988       {
    989       case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED:
    990         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    991                     "Expiration too low, initiating payment\n");
    992         return prepare_payment (puc);
    993       case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR:
    994         return TALER_MHD_reply_with_error (puc->con,
    995                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    996                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
    997                                            NULL);
    998       case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS:
    999         /* continue below */
   1000         break;
   1001       case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED:
   1002         if (0 == GNUNET_memcmp (&hash,
   1003                                 &puc->new_policy_upload_hash))
   1004         {
   1005           /* Refuse upload: we already have that backup! */
   1006           struct MHD_Response *resp;
   1007           MHD_RESULT ret;
   1008           char version_s[14];
   1009 
   1010           GNUNET_snprintf (version_s,
   1011                            sizeof (version_s),
   1012                            "%u",
   1013                            (unsigned int) version);
   1014           resp = MHD_create_response_from_buffer (0,
   1015                                                   NULL,
   1016                                                   MHD_RESPMEM_PERSISTENT);
   1017           TALER_MHD_add_global_headers (resp,
   1018                                         false);
   1019           GNUNET_break (MHD_YES ==
   1020                         MHD_add_response_header (resp,
   1021                                                  ANASTASIS_HTTP_HEADER_POLICY_VERSION,
   1022                                                  version_s));
   1023           ret = MHD_queue_response (connection,
   1024                                     MHD_HTTP_NOT_MODIFIED,
   1025                                     resp);
   1026           GNUNET_break (MHD_YES == ret);
   1027           MHD_destroy_response (resp);
   1028           return ret;
   1029         }
   1030         break;
   1031       }
   1032     }
   1033     /* ready to begin! */
   1034     return MHD_YES;
   1035   }
   1036 
   1037   if (NULL != puc->resp)
   1038   {
   1039     MHD_RESULT ret;
   1040 
   1041     /* We generated a response asynchronously, queue that */
   1042     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1043                 "Returning asynchronously generated response with HTTP status %u\n",
   1044                 puc->response_code);
   1045     ret = MHD_queue_response (connection,
   1046                               puc->response_code,
   1047                               puc->resp);
   1048     GNUNET_break (MHD_YES == ret);
   1049     MHD_destroy_response (puc->resp);
   1050     puc->resp = NULL;
   1051     return ret;
   1052   }
   1053 
   1054   /* handle upload */
   1055   if (0 != *recovery_data_size)
   1056   {
   1057     /* check MHD invariant */
   1058     GNUNET_assert (puc->upload_off + *recovery_data_size <= puc->upload_size);
   1059     memcpy (&puc->upload[puc->upload_off],
   1060             recovery_data,
   1061             *recovery_data_size);
   1062     puc->upload_off += *recovery_data_size;
   1063     GNUNET_CRYPTO_hash_context_read (puc->hash_ctx,
   1064                                      recovery_data,
   1065                                      *recovery_data_size);
   1066     *recovery_data_size = 0;
   1067     return MHD_YES;
   1068   }
   1069 
   1070   if ( (0 == puc->upload_off) &&
   1071        (0 != puc->upload_size) &&
   1072        (NULL == puc->resp) )
   1073   {
   1074     /* wait for upload */
   1075     return MHD_YES;
   1076   }
   1077 
   1078   /* finished with upload, check hash */
   1079   if (NULL != puc->hash_ctx)
   1080   {
   1081     struct GNUNET_HashCode our_hash;
   1082 
   1083     GNUNET_CRYPTO_hash_context_finish (puc->hash_ctx,
   1084                                        &our_hash);
   1085     puc->hash_ctx = NULL;
   1086     if (0 != GNUNET_memcmp (&our_hash,
   1087                             &puc->new_policy_upload_hash))
   1088     {
   1089       GNUNET_break_op (0);
   1090       return TALER_MHD_reply_with_error (connection,
   1091                                          MHD_HTTP_BAD_REQUEST,
   1092                                          TALER_EC_ANASTASIS_POLICY_INVALID_UPLOAD,
   1093                                          "Data uploaded does not match Etag promise");
   1094     }
   1095   }
   1096 
   1097   /* store backup to database */
   1098   {
   1099     enum ANASTASIS_DB_StoreStatus ss;
   1100     uint32_t version = UINT32_MAX;
   1101     char version_s[14];
   1102     char expir_s[32];
   1103 
   1104     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1105                 "Uploading recovery document\n");
   1106     ss = db->store_recovery_document (db->cls,
   1107                                       &puc->account,
   1108                                       &puc->account_sig,
   1109                                       &puc->new_policy_upload_hash,
   1110                                       puc->upload,
   1111                                       puc->upload_size,
   1112                                       puc->meta_data,
   1113                                       puc->meta_data_size,
   1114                                       &puc->payment_identifier,
   1115                                       &version);
   1116     GNUNET_snprintf (version_s,
   1117                      sizeof (version_s),
   1118                      "%u",
   1119                      (unsigned int) version);
   1120     GNUNET_snprintf (expir_s,
   1121                      sizeof (expir_s),
   1122                      "%llu",
   1123                      (unsigned long long)
   1124                      (puc->paid_until.abs_time.abs_value_us
   1125                       / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
   1126     switch (ss)
   1127     {
   1128     case ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED:
   1129       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1130                   "Storage request limit exceeded, requesting payment\n");
   1131       if (! puc->payment_identifier_provided)
   1132       {
   1133         GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
   1134                                     &puc->payment_identifier,
   1135                                     sizeof (struct ANASTASIS_PaymentSecretP));
   1136         puc->payment_identifier_provided = true;
   1137         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1138                     "Also no payment identifier, requesting payment\n");
   1139       }
   1140       return begin_payment (puc);
   1141     case ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED:
   1142       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1143                   "Policy store operation requires payment\n");
   1144       if (! puc->payment_identifier_provided)
   1145       {
   1146         GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
   1147                                     &puc->payment_identifier,
   1148                                     sizeof (struct ANASTASIS_PaymentSecretP));
   1149         puc->payment_identifier_provided = true;
   1150       }
   1151       return begin_payment (puc);
   1152     case ANASTASIS_DB_STORE_STATUS_HARD_ERROR:
   1153     case ANASTASIS_DB_STORE_STATUS_SOFT_ERROR:
   1154       return TALER_MHD_reply_with_error (puc->con,
   1155                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
   1156                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
   1157                                          NULL);
   1158     case ANASTASIS_DB_STORE_STATUS_NO_RESULTS:
   1159       {
   1160         /* database says nothing actually changed, 304 (could
   1161            theoretically happen if another equivalent upload succeeded
   1162            since we last checked!) */
   1163         struct MHD_Response *resp;
   1164         MHD_RESULT ret;
   1165 
   1166         resp = MHD_create_response_from_buffer (0,
   1167                                                 NULL,
   1168                                                 MHD_RESPMEM_PERSISTENT);
   1169         TALER_MHD_add_global_headers (resp,
   1170                                       false);
   1171         GNUNET_break (MHD_YES ==
   1172                       MHD_add_response_header (resp,
   1173                                                "Anastasis-Version",
   1174                                                version_s));
   1175         ret = MHD_queue_response (connection,
   1176                                   MHD_HTTP_NOT_MODIFIED,
   1177                                   resp);
   1178         GNUNET_break (MHD_YES == ret);
   1179         MHD_destroy_response (resp);
   1180         return ret;
   1181       }
   1182     case ANASTASIS_DB_STORE_STATUS_SUCCESS:
   1183       /* generate main (204) standard success reply */
   1184       {
   1185         struct MHD_Response *resp;
   1186         MHD_RESULT ret;
   1187 
   1188         resp = MHD_create_response_from_buffer (0,
   1189                                                 NULL,
   1190                                                 MHD_RESPMEM_PERSISTENT);
   1191         TALER_MHD_add_global_headers (resp,
   1192                                       false);
   1193         GNUNET_break (MHD_YES ==
   1194                       MHD_add_response_header (resp,
   1195                                                ANASTASIS_HTTP_HEADER_POLICY_VERSION,
   1196                                                version_s));
   1197         GNUNET_break (MHD_YES ==
   1198                       MHD_add_response_header (resp,
   1199                                                ANASTASIS_HTTP_HEADER_POLICY_EXPIRATION,
   1200                                                expir_s));
   1201         ret = MHD_queue_response (connection,
   1202                                   MHD_HTTP_NO_CONTENT,
   1203                                   resp);
   1204         GNUNET_break (MHD_YES == ret);
   1205         MHD_destroy_response (resp);
   1206         return ret;
   1207       }
   1208     }
   1209   }
   1210   GNUNET_break (0);
   1211   return MHD_NO;
   1212 }