anastasis

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

anastasis-httpd_policy-upload.c (41062B)


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