anastasis

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

anastasis-httpd_policy-upload.c (40688B)


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