anastasis

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

anastasis-httpd_policy-upload.c (43045B)


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