anastasis

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

anastasis-httpd_policy-upload.c (39937B)


      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 = ANASTASIS_DB_record_recdoc_payment (
    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 = ANASTASIS_DB_increment_lifetime (
    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       &puc->payment_identifier,
    648       sizeof (struct ANASTASIS_PaymentSecretP));
    649     puc->payment_identifier_provided = true;
    650     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    651                 "No payment identifier, initiating payment\n");
    652     return begin_payment (puc);
    653   }
    654   await_payment (puc);
    655   return MHD_YES;
    656 }
    657 
    658 
    659 enum MHD_Result
    660 AH_handler_policy_post (
    661   struct MHD_Connection *connection,
    662   struct TM_HandlerContext *hc,
    663   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub,
    664   const char *recovery_data,
    665   size_t *recovery_data_size)
    666 {
    667   struct PolicyUploadContext *puc = hc->ctx;
    668 
    669   if (NULL == puc)
    670   {
    671     /* first call, setup internals */
    672     puc = GNUNET_new (struct PolicyUploadContext);
    673     hc->ctx = puc;
    674     hc->cc = &cleanup_ctx;
    675     puc->con = connection;
    676 
    677     TALER_MHD_parse_request_header_auto (connection,
    678                                          ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER,
    679                                          &puc->payment_identifier,
    680                                          puc->payment_identifier_provided);
    681     puc->account = *account_pub;
    682 
    683     /* check for meta-data */
    684     {
    685       const char *metas;
    686 
    687       metas = MHD_lookup_connection_value (connection,
    688                                            MHD_HEADER_KIND,
    689                                            ANASTASIS_HTTP_HEADER_POLICY_META_DATA);
    690       if (NULL == metas)
    691       {
    692         GNUNET_break_op (0);
    693         return TALER_MHD_reply_with_error (
    694           connection,
    695           MHD_HTTP_BAD_REQUEST,
    696           TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    697           ANASTASIS_HTTP_HEADER_POLICY_META_DATA
    698           " header must be present");
    699       }
    700       if (GNUNET_OK !=
    701           GNUNET_STRINGS_string_to_data_alloc (metas,
    702                                                strlen (metas),
    703                                                &puc->meta_data,
    704                                                &puc->meta_data_size))
    705       {
    706         GNUNET_break_op (0);
    707         return TALER_MHD_reply_with_error (
    708           connection,
    709           MHD_HTTP_BAD_REQUEST,
    710           TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    711           ANASTASIS_HTTP_HEADER_POLICY_META_DATA
    712           " header must include a base32-encoded value");
    713       }
    714     }
    715     /* now setup 'puc' */
    716     {
    717       const char *lens;
    718       unsigned long len;
    719       char dummy;
    720 
    721       lens = MHD_lookup_connection_value (connection,
    722                                           MHD_HEADER_KIND,
    723                                           MHD_HTTP_HEADER_CONTENT_LENGTH);
    724       if ( (NULL == lens) ||
    725            (1 != sscanf (lens,
    726                          "%lu%c",
    727                          &len,
    728                          &dummy)) )
    729       {
    730         GNUNET_break_op (0);
    731         return TALER_MHD_reply_with_error (
    732           connection,
    733           MHD_HTTP_BAD_REQUEST,
    734           (NULL == lens)
    735           ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH
    736           : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH,
    737           NULL);
    738       }
    739       if (len / 1024 / 1024 >= AH_upload_limit_mb)
    740       {
    741         GNUNET_break_op (0);
    742         return TALER_MHD_reply_with_error (connection,
    743                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
    744                                            TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH,
    745                                            "Content-length value not acceptable");
    746       }
    747       puc->upload = GNUNET_malloc_large (len);
    748       if (NULL == puc->upload)
    749       {
    750         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    751                              "malloc");
    752         return TALER_MHD_reply_with_error (connection,
    753                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
    754                                            TALER_EC_ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH,
    755                                            NULL);
    756       }
    757       puc->upload_size = (size_t) len;
    758     }
    759 
    760     TALER_MHD_parse_request_header_auto_t (connection,
    761                                            ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE,
    762                                            &puc->account_sig);
    763     {
    764       /* Check if header contains an ETAG */
    765       const char *etag;
    766 
    767       etag = MHD_lookup_connection_value (connection,
    768                                           MHD_HEADER_KIND,
    769                                           MHD_HTTP_HEADER_IF_NONE_MATCH);
    770       if ( (NULL == etag) ||
    771            (2 >= strlen (etag)) ||
    772            ('"' != etag[0]) ||
    773            ('"' != etag[strlen (etag) - 1]) ||
    774            (GNUNET_OK !=
    775             GNUNET_STRINGS_string_to_data (etag + 1,
    776                                            strlen (etag) - 2,
    777                                            &puc->new_policy_upload_hash,
    778                                            sizeof (puc->new_policy_upload_hash))
    779            ) )
    780       {
    781         GNUNET_break_op (0);
    782         return TALER_MHD_reply_with_error (connection,
    783                                            MHD_HTTP_BAD_REQUEST,
    784                                            TALER_EC_ANASTASIS_POLICY_BAD_IF_MATCH,
    785                                            MHD_HTTP_HEADER_IF_NONE_MATCH
    786                                            " header must include a base32-encoded SHA-512 hash");
    787       }
    788     }
    789     /* validate signature */
    790     {
    791       struct ANASTASIS_UploadSignaturePS usp = {
    792         .purpose.size = htonl (sizeof (usp)),
    793         .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD),
    794         .new_recovery_data_hash = puc->new_policy_upload_hash
    795       };
    796 
    797       if (GNUNET_OK !=
    798           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD,
    799                                       &usp,
    800                                       &puc->account_sig.eddsa_sig,
    801                                       &account_pub->pub))
    802       {
    803         GNUNET_break_op (0);
    804         return TALER_MHD_reply_with_error (connection,
    805                                            MHD_HTTP_FORBIDDEN,
    806                                            TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE,
    807                                            ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE);
    808       }
    809     }
    810 
    811     puc->timeout = GNUNET_TIME_relative_to_absolute (
    812       CHECK_PAYMENT_GENERIC_TIMEOUT);
    813     TALER_MHD_parse_request_timeout (connection,
    814                                      &puc->timeout);
    815 
    816     /* check if the client insists on paying */
    817     {
    818       const char *req;
    819       unsigned int years;
    820 
    821       req = MHD_lookup_connection_value (connection,
    822                                          MHD_GET_ARGUMENT_KIND,
    823                                          "storage_duration");
    824       if (NULL != req)
    825       {
    826         char dummy;
    827 
    828         if (1 != sscanf (req,
    829                          "%u%c",
    830                          &years,
    831                          &dummy))
    832         {
    833           GNUNET_break_op (0);
    834           return TALER_MHD_reply_with_error (connection,
    835                                              MHD_HTTP_BAD_REQUEST,
    836                                              TALER_EC_GENERIC_PARAMETER_MALFORMED,
    837                                              "storage_duration (must be non-negative number)");
    838         }
    839       }
    840       else
    841       {
    842         years = 1;
    843       }
    844       puc->end_date = GNUNET_TIME_relative_to_timestamp (
    845         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    846                                        years));
    847     }
    848 
    849     /* get ready to hash (done here as we may go async for payments next) */
    850     puc->hash_ctx = GNUNET_CRYPTO_hash_context_start ();
    851 
    852     /* Check database to see if the transaction is permissible */
    853     {
    854       struct GNUNET_TIME_Relative rem;
    855 
    856       rem = GNUNET_TIME_absolute_get_remaining (puc->end_date.abs_time);
    857       puc->years_to_pay = rem.rel_value_us
    858                           / GNUNET_TIME_UNIT_YEARS.rel_value_us;
    859       if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us))
    860         puc->years_to_pay++;
    861       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    862                   "Calculated years to pay to be %u until %s\n",
    863                   puc->years_to_pay,
    864                   GNUNET_TIME_absolute2s (puc->end_date.abs_time));
    865 
    866       if (puc->payment_identifier_provided)
    867       {
    868         /* check if payment identifier is valid (existing and paid) */
    869         bool paid;
    870         bool valid_counter;
    871         enum GNUNET_DB_QueryStatus qs;
    872 
    873         qs = ANASTASIS_DB_check_payment_identifier (
    874           &puc->payment_identifier,
    875           &paid,
    876           &valid_counter);
    877         if (qs < 0)
    878           return TALER_MHD_reply_with_error (puc->con,
    879                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    880                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    881                                              NULL);
    882 
    883         if ( (! paid) ||
    884              (! valid_counter) )
    885         {
    886           if (! valid_counter)
    887           {
    888             puc->payment_identifier_provided = false;
    889             if (0 == puc->years_to_pay)
    890               puc->years_to_pay = 1;
    891             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    892                         "Too many uploads with this payment identifier, initiating fresh payment\n");
    893           }
    894           else
    895           {
    896             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    897                         "Given payment identifier not known to be paid, initiating payment\n");
    898           }
    899           return prepare_payment (puc);
    900         }
    901       }
    902 
    903       if (! puc->payment_identifier_provided)
    904       {
    905         enum GNUNET_DB_QueryStatus qs;
    906         struct GNUNET_TIME_Relative rel;
    907 
    908         /* generate fresh payment identifier */
    909         GNUNET_CRYPTO_random_block (&puc->payment_identifier,
    910                                     sizeof (struct ANASTASIS_PaymentSecretP));
    911         if (! TALER_amount_is_zero (&AH_annual_fee))
    912         {
    913           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    914                       "No payment identifier, requesting payment\n");
    915           return begin_payment (puc);
    916         }
    917         /* Cost is zero, fake "zero" payment having happened */
    918         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    919                     "Policy upload is free, allowing upload without payment\n");
    920         qs = ANASTASIS_DB_record_recdoc_payment (
    921           account_pub,
    922           AH_post_counter,
    923           &puc->payment_identifier,
    924           &AH_annual_fee);
    925         if (qs <= 0)
    926           return TALER_MHD_reply_with_error (puc->con,
    927                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    928                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    929                                              NULL);
    930         rel = GNUNET_TIME_relative_multiply (
    931           GNUNET_TIME_UNIT_YEARS,
    932           ANASTASIS_MAX_YEARS_STORAGE);
    933         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    934                     "Policy lifetime is %s (%u years)\n",
    935                     GNUNET_TIME_relative2s (rel,
    936                                             true),
    937                     ANASTASIS_MAX_YEARS_STORAGE);
    938         puc->paid_until = GNUNET_TIME_relative_to_timestamp (rel);
    939         qs = ANASTASIS_DB_update_lifetime (
    940           account_pub,
    941           &puc->payment_identifier,
    942           puc->paid_until);
    943         if (qs <= 0)
    944         {
    945           GNUNET_break (0);
    946           return TALER_MHD_reply_with_error (puc->con,
    947                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    948                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    949                                              NULL);
    950         }
    951       }
    952     }
    953 
    954     /* Check if existing policy matches upload (and if, skip it) */
    955     {
    956       struct GNUNET_HashCode hash;
    957       enum ANASTASIS_DB_AccountStatus as;
    958       uint32_t version;
    959       struct GNUNET_TIME_Timestamp now;
    960       struct GNUNET_TIME_Relative rem;
    961 
    962       as = ANASTASIS_DB_lookup_account (
    963         account_pub,
    964         &puc->paid_until,
    965         &hash,
    966         &version);
    967       now = GNUNET_TIME_timestamp_get ();
    968       if (GNUNET_TIME_timestamp_cmp (puc->paid_until,
    969                                      <,
    970                                      now))
    971         puc->paid_until = now;
    972       rem = GNUNET_TIME_absolute_get_difference (puc->paid_until.abs_time,
    973                                                  puc->end_date.abs_time);
    974       puc->years_to_pay = rem.rel_value_us
    975                           / GNUNET_TIME_UNIT_YEARS.rel_value_us;
    976       if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us))
    977         puc->years_to_pay++;
    978       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    979                   "Calculated years to pay to be %u until %s\n",
    980                   puc->years_to_pay,
    981                   GNUNET_TIME_absolute2s (puc->end_date.abs_time));
    982       if ( (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED == as) &&
    983            (0 != puc->years_to_pay) )
    984       {
    985         /* user requested extension, force payment */
    986         as = ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED;
    987       }
    988       switch (as)
    989       {
    990       case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED:
    991         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    992                     "Expiration too low, initiating payment\n");
    993         return prepare_payment (puc);
    994       case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR:
    995         return TALER_MHD_reply_with_error (puc->con,
    996                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    997                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
    998                                            NULL);
    999       case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS:
   1000         /* continue below */
   1001         break;
   1002       case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED:
   1003         if (0 == GNUNET_memcmp (&hash,
   1004                                 &puc->new_policy_upload_hash))
   1005         {
   1006           /* Refuse upload: we already have that backup! */
   1007           struct MHD_Response *resp;
   1008           enum MHD_Result ret;
   1009           char version_s[14];
   1010 
   1011           GNUNET_snprintf (version_s,
   1012                            sizeof (version_s),
   1013                            "%u",
   1014                            (unsigned int) version);
   1015           resp = MHD_create_response_from_buffer (0,
   1016                                                   NULL,
   1017                                                   MHD_RESPMEM_PERSISTENT);
   1018           TALER_MHD_add_global_headers (resp,
   1019                                         false);
   1020           GNUNET_break (MHD_YES ==
   1021                         MHD_add_response_header (resp,
   1022                                                  ANASTASIS_HTTP_HEADER_POLICY_VERSION,
   1023                                                  version_s));
   1024           ret = MHD_queue_response (connection,
   1025                                     MHD_HTTP_NOT_MODIFIED,
   1026                                     resp);
   1027           GNUNET_break (MHD_YES == ret);
   1028           MHD_destroy_response (resp);
   1029           return ret;
   1030         }
   1031         break;
   1032       }
   1033     }
   1034     /* ready to begin! */
   1035     return MHD_YES;
   1036   }
   1037 
   1038   if (NULL != puc->resp)
   1039   {
   1040     enum MHD_Result ret;
   1041 
   1042     /* We generated a response asynchronously, queue that */
   1043     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1044                 "Returning asynchronously generated response with HTTP status %u\n",
   1045                 puc->response_code);
   1046     ret = MHD_queue_response (connection,
   1047                               puc->response_code,
   1048                               puc->resp);
   1049     GNUNET_break (MHD_YES == ret);
   1050     MHD_destroy_response (puc->resp);
   1051     puc->resp = NULL;
   1052     return ret;
   1053   }
   1054 
   1055   /* handle upload */
   1056   if (0 != *recovery_data_size)
   1057   {
   1058     /* check MHD invariant */
   1059     GNUNET_assert (puc->upload_off + *recovery_data_size <= puc->upload_size);
   1060     memcpy (&puc->upload[puc->upload_off],
   1061             recovery_data,
   1062             *recovery_data_size);
   1063     puc->upload_off += *recovery_data_size;
   1064     GNUNET_CRYPTO_hash_context_read (puc->hash_ctx,
   1065                                      recovery_data,
   1066                                      *recovery_data_size);
   1067     *recovery_data_size = 0;
   1068     return MHD_YES;
   1069   }
   1070 
   1071   if ( (0 == puc->upload_off) &&
   1072        (0 != puc->upload_size) &&
   1073        (NULL == puc->resp) )
   1074   {
   1075     /* wait for upload */
   1076     return MHD_YES;
   1077   }
   1078 
   1079   /* finished with upload, check hash */
   1080   if (NULL != puc->hash_ctx)
   1081   {
   1082     struct GNUNET_HashCode our_hash;
   1083 
   1084     GNUNET_CRYPTO_hash_context_finish (puc->hash_ctx,
   1085                                        &our_hash);
   1086     puc->hash_ctx = NULL;
   1087     if (0 != GNUNET_memcmp (&our_hash,
   1088                             &puc->new_policy_upload_hash))
   1089     {
   1090       GNUNET_break_op (0);
   1091       return TALER_MHD_reply_with_error (connection,
   1092                                          MHD_HTTP_BAD_REQUEST,
   1093                                          TALER_EC_ANASTASIS_POLICY_INVALID_UPLOAD,
   1094                                          "Data uploaded does not match Etag promise");
   1095     }
   1096   }
   1097 
   1098   /* store backup to database */
   1099   {
   1100     enum ANASTASIS_DB_StoreStatus ss;
   1101     uint32_t version = UINT32_MAX;
   1102     char version_s[14];
   1103     char expir_s[32];
   1104 
   1105     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1106                 "Uploading recovery document\n");
   1107     ss = ANASTASIS_DB_store_recovery_document (
   1108       &puc->account,
   1109       &puc->account_sig,
   1110       &puc->new_policy_upload_hash,
   1111       puc->upload,
   1112       puc->upload_size,
   1113       puc->meta_data,
   1114       puc->meta_data_size,
   1115       &puc->payment_identifier,
   1116       &version);
   1117     GNUNET_snprintf (version_s,
   1118                      sizeof (version_s),
   1119                      "%u",
   1120                      (unsigned int) version);
   1121     GNUNET_snprintf (expir_s,
   1122                      sizeof (expir_s),
   1123                      "%llu",
   1124                      (unsigned long long)
   1125                      (puc->paid_until.abs_time.abs_value_us
   1126                       / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
   1127     switch (ss)
   1128     {
   1129     case ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED:
   1130       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1131                   "Storage request limit exceeded, requesting payment\n");
   1132       if (! puc->payment_identifier_provided)
   1133       {
   1134         GNUNET_CRYPTO_random_block (&puc->payment_identifier,
   1135                                     sizeof (struct ANASTASIS_PaymentSecretP));
   1136         puc->payment_identifier_provided = true;
   1137         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1138                     "Also no payment identifier, requesting payment\n");
   1139       }
   1140       return begin_payment (puc);
   1141     case ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED:
   1142       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1143                   "Policy store operation requires payment\n");
   1144       if (! puc->payment_identifier_provided)
   1145       {
   1146         GNUNET_CRYPTO_random_block (&puc->payment_identifier,
   1147                                     sizeof (struct ANASTASIS_PaymentSecretP));
   1148         puc->payment_identifier_provided = true;
   1149       }
   1150       return begin_payment (puc);
   1151     case ANASTASIS_DB_STORE_STATUS_HARD_ERROR:
   1152     case ANASTASIS_DB_STORE_STATUS_SOFT_ERROR:
   1153       return TALER_MHD_reply_with_error (puc->con,
   1154                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
   1155                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
   1156                                          NULL);
   1157     case ANASTASIS_DB_STORE_STATUS_NO_RESULTS:
   1158       {
   1159         /* database says nothing actually changed, 304 (could
   1160            theoretically happen if another equivalent upload succeeded
   1161            since we last checked!) */
   1162         struct MHD_Response *resp;
   1163         enum MHD_Result ret;
   1164 
   1165         resp = MHD_create_response_from_buffer (0,
   1166                                                 NULL,
   1167                                                 MHD_RESPMEM_PERSISTENT);
   1168         TALER_MHD_add_global_headers (resp,
   1169                                       false);
   1170         GNUNET_break (MHD_YES ==
   1171                       MHD_add_response_header (resp,
   1172                                                "Anastasis-Version",
   1173                                                version_s));
   1174         ret = MHD_queue_response (connection,
   1175                                   MHD_HTTP_NOT_MODIFIED,
   1176                                   resp);
   1177         GNUNET_break (MHD_YES == ret);
   1178         MHD_destroy_response (resp);
   1179         return ret;
   1180       }
   1181     case ANASTASIS_DB_STORE_STATUS_SUCCESS:
   1182       /* generate main (204) standard success reply */
   1183       {
   1184         struct MHD_Response *resp;
   1185         enum MHD_Result ret;
   1186 
   1187         resp = MHD_create_response_from_buffer (0,
   1188                                                 NULL,
   1189                                                 MHD_RESPMEM_PERSISTENT);
   1190         TALER_MHD_add_global_headers (resp,
   1191                                       false);
   1192         GNUNET_break (MHD_YES ==
   1193                       MHD_add_response_header (resp,
   1194                                                ANASTASIS_HTTP_HEADER_POLICY_VERSION,
   1195                                                version_s));
   1196         GNUNET_break (MHD_YES ==
   1197                       MHD_add_response_header (resp,
   1198                                                ANASTASIS_HTTP_HEADER_POLICY_EXPIRATION,
   1199                                                expir_s));
   1200         ret = MHD_queue_response (connection,
   1201                                   MHD_HTTP_NO_CONTENT,
   1202                                   resp);
   1203         GNUNET_break (MHD_YES == ret);
   1204         MHD_destroy_response (resp);
   1205         return ret;
   1206       }
   1207     }
   1208   }
   1209   GNUNET_break (0);
   1210   return MHD_NO;
   1211 }