anastasis

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

anastasis-httpd_policy-upload.c (43896B)


      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   /* The row records the asking price in the primary currency: which of
    370      the offered currencies the wallet will settle in is not known until
    371      it actually pays. */
    372   qs = ANASTASIS_DB_do_insert_recdoc_payment (
    373     &puc->account,
    374     (uint32_t) AH_post_counter,
    375     &puc->payment_identifier,
    376     AH_primary_price (&AH_annual_fees));
    377   if (0 >= qs)
    378   {
    379     GNUNET_break (0);
    380     puc->resp = TALER_MHD_make_error (
    381       TALER_EC_GENERIC_DB_STORE_FAILED,
    382       "insert recdoc payment");
    383     puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    384     return;
    385   }
    386   if (GNUNET_OK !=
    387       make_payment_request (puc))
    388   {
    389     GNUNET_break (0);
    390     puc->resp = TALER_MHD_make_error (
    391       TALER_EC_GENERIC_DB_STORE_FAILED,
    392       "failed to initiate payment");
    393     puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    394   }
    395 }
    396 
    397 
    398 /**
    399  * Callback to process a GET /check-payment request
    400  *
    401  * @param cls our `struct PolicyUploadContext`
    402  * @param osr order status
    403  */
    404 static void
    405 check_payment_cb (struct PolicyUploadContext *puc,
    406                   const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    407 {
    408   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
    409 
    410   /* refunds are not supported, verify */
    411   puc->cpo = NULL;
    412   GNUNET_CONTAINER_DLL_remove (puc_head,
    413                                puc_tail,
    414                                puc);
    415   MHD_resume_connection (puc->con);
    416   AH_trigger_daemon (NULL);
    417   switch (hr->http_status)
    418   {
    419   case MHD_HTTP_OK:
    420     GNUNET_assert (NULL != osr);
    421     break; /* processed below */
    422   case MHD_HTTP_UNAUTHORIZED:
    423     puc->resp = TALER_MHD_make_error (
    424       TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED,
    425       NULL);
    426     puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    427     return;
    428   default:
    429     puc->resp = TALER_MHD_make_error (
    430       TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR,
    431       "failed to initiate payment");
    432     puc->response_code = MHD_HTTP_BAD_GATEWAY;
    433     return;
    434   }
    435 
    436   GNUNET_assert (MHD_HTTP_OK == hr->http_status);
    437   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    438               "Payment status checked: %d\n",
    439               osr->details.ok.status);
    440   switch (osr->details.ok.status)
    441   {
    442   case TALER_MERCHANT_OSC_PAID:
    443     {
    444       enum GNUNET_DB_QueryStatus qs;
    445       unsigned int years;
    446       struct GNUNET_TIME_Relative paid_until;
    447       struct TALER_Amount amount;
    448       const struct TALER_Amount *fee;
    449 
    450       if (GNUNET_OK !=
    451           AH_paid_amount (osr,
    452                           &amount))
    453       {
    454         GNUNET_break (0);
    455         puc->resp = TALER_MHD_make_error (
    456           TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    457           "no amount given");
    458         puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    459         return; /* continue as planned */
    460       }
    461       /* Divide by the fee in the currency actually paid, not by "the"
    462          fee: with several on offer the two need not be the same one.
    463          Reachable in production if an operator drops a currency while an
    464          order priced in it is still unpaid, hence an error and not an
    465          assertion. */
    466       fee = TALER_amount_list_find (&AH_annual_fees,
    467                                     amount.currency);
    468       if (NULL == fee)
    469       {
    470         GNUNET_break (0);
    471         puc->resp = TALER_MHD_make_error (
    472           TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR,
    473           "order was paid in a currency this provider does not offer");
    474         puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    475         return; /* continue as planned */
    476       }
    477       years = TALER_amount_divide2 (&amount,
    478                                     fee);
    479       paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    480                                                   years);
    481       /* add 1 week grace period, otherwise if a user
    482          wants to pay for 1 year, the first seconds
    483          would have passed between making the payment
    484          and our subsequent check if +1 year was
    485          paid... So we actually say 1 year = 52 weeks
    486          on the server, while the client calculates
    487          with 365 days. */
    488       paid_until = GNUNET_TIME_relative_add (paid_until,
    489                                              GNUNET_TIME_UNIT_WEEKS);
    490 
    491       qs = ANASTASIS_DB_do_update_account_lifetime (
    492         &puc->account,
    493         &puc->payment_identifier,
    494         GNUNET_TIME_timestamp_get (),
    495         paid_until,
    496         &puc->paid_until);
    497       if (0 <= qs)
    498         return; /* continue as planned */
    499       GNUNET_break (0);
    500       puc->resp = TALER_MHD_make_error (
    501         TALER_EC_GENERIC_DB_FETCH_FAILED,
    502         "do update account lifetime");
    503       puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    504       return; /* continue as planned */
    505     }
    506   case TALER_MERCHANT_OSC_UNPAID:
    507   case TALER_MERCHANT_OSC_CLAIMED:
    508     break;
    509   }
    510   if (! GNUNET_TIME_absolute_is_zero (puc->existing_pi_timestamp.abs_time))
    511   {
    512     /* repeat payment request */
    513     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    514                 "Repeating payment request\n");
    515     if (GNUNET_OK !=
    516         make_payment_request (puc))
    517     {
    518       GNUNET_break (0);
    519       puc->resp = TALER_MHD_make_error (
    520         TALER_EC_GENERIC_DB_STORE_FAILED,
    521         "failed to initiate payment");
    522       puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    523     }
    524     return;
    525   }
    526   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    527               "Timeout waiting for payment\n");
    528   puc->resp = TALER_MHD_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT,
    529                                     "Timeout awaiting promised payment");
    530   GNUNET_assert (NULL != puc->resp);
    531   puc->response_code = MHD_HTTP_REQUEST_TIMEOUT;
    532 }
    533 
    534 
    535 /**
    536  * Helper function used to ask our backend to await
    537  * a payment for the user's account.
    538  *
    539  * @param puc context to begin payment for.
    540  */
    541 static void
    542 await_payment (struct PolicyUploadContext *puc)
    543 {
    544   struct GNUNET_TIME_Relative timeout
    545     = GNUNET_TIME_absolute_get_remaining (puc->timeout);
    546 
    547   GNUNET_CONTAINER_DLL_insert (puc_head,
    548                                puc_tail,
    549                                puc);
    550   MHD_suspend_connection (puc->con);
    551   {
    552     char *order_id;
    553 
    554     order_id = GNUNET_STRINGS_data_to_string_alloc (
    555       &puc->payment_identifier,
    556       sizeof(struct ANASTASIS_PaymentSecretP));
    557     puc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx,
    558                                                         AH_backend_url,
    559                                                         order_id);
    560     GNUNET_assert (NULL != puc->cpo);
    561     GNUNET_free (order_id);
    562     GNUNET_assert (
    563       GNUNET_OK ==
    564       TALER_MERCHANT_get_private_order_set_options (
    565         puc->cpo,
    566         TALER_MERCHANT_get_private_order_option_timeout (timeout)));
    567     GNUNET_assert (
    568       TALER_EC_NONE ==
    569       TALER_MERCHANT_get_private_order_start (puc->cpo,
    570                                               &check_payment_cb,
    571                                               puc));
    572   }
    573   AH_trigger_curl ();
    574 }
    575 
    576 
    577 /**
    578  * Helper function used to ask our backend to begin processing a
    579  * payment for the user's account.  May perform asynchronous
    580  * operations by suspending the connection if required.
    581  *
    582  * @param puc context to begin payment for.
    583  * @return MHD status code
    584  */
    585 static enum MHD_Result
    586 begin_payment (struct PolicyUploadContext *puc)
    587 {
    588   json_t *order;
    589 
    590   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    591               "Suspending connection while creating order at `%s'\n",
    592               AH_backend_url);
    593   {
    594     char *order_id;
    595     struct TALER_AmountList upload_fees;
    596 
    597     TALER_amount_list_copy (&upload_fees,
    598                             &AH_annual_fees);
    599     if (GNUNET_OK !=
    600         TALER_amount_list_multiply (&upload_fees,
    601                                     puc->years_to_pay))
    602     {
    603       GNUNET_break_op (0);
    604       TALER_amount_list_free (&upload_fees);
    605       return TALER_MHD_reply_with_error (puc->con,
    606                                          MHD_HTTP_BAD_REQUEST,
    607                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    608                                          "storage_duration_years");
    609     }
    610 
    611     order_id = GNUNET_STRINGS_data_to_string_alloc (
    612       &puc->payment_identifier,
    613       sizeof(struct ANASTASIS_PaymentSecretP));
    614     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    615                 "Creating order for %u years with payment of %s\n",
    616                 puc->years_to_pay,
    617                 TALER_amount_list2s (&upload_fees));
    618     order = AH_make_order (order_id,
    619                            "Anastasis policy storage fee",
    620                            &upload_fees);
    621     TALER_amount_list_free (&upload_fees);
    622     GNUNET_free (order_id);
    623     GNUNET_assert (0 ==
    624                    json_object_set_new (
    625                      order,
    626                      "products",
    627                      json_pack ("[{s:s,s:I,s:s}]",
    628                                 "description", "policy storage fee",
    629                                 "quantity", (json_int_t) puc->years_to_pay,
    630                                 "unit", "years")));
    631   }
    632   /* Only link into the list once we are actually about to suspend: the list
    633      exists to resume suspended connections at shutdown, and cleanup_ctx()
    634      frees the context without unlinking it, so an error return above would
    635      otherwise leave a dangling entry behind. */
    636   GNUNET_CONTAINER_DLL_insert (puc_head,
    637                                puc_tail,
    638                                puc);
    639   MHD_suspend_connection (puc->con);
    640   puc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx,
    641                                                        AH_backend_url,
    642                                                        order);
    643   GNUNET_assert (NULL != puc->po);
    644   GNUNET_assert (
    645     GNUNET_OK ==
    646     TALER_MERCHANT_post_private_orders_set_options (
    647       puc->po,
    648       TALER_MERCHANT_post_private_orders_option_create_token (false)));
    649   GNUNET_assert (TALER_EC_NONE ==
    650                  TALER_MERCHANT_post_private_orders_start (puc->po,
    651                                                            &proposal_cb,
    652                                                            puc));
    653   AH_trigger_curl ();
    654   json_decref (order);
    655   return MHD_YES;
    656 }
    657 
    658 
    659 /**
    660  * Prepare to receive a payment, possibly requesting it, or just waiting
    661  * for it to be completed by the client.
    662  *
    663  * @param puc context to prepare payment for
    664  * @return MHD status
    665  */
    666 static enum MHD_Result
    667 prepare_payment (struct PolicyUploadContext *puc)
    668 {
    669   if (! puc->payment_identifier_provided)
    670   {
    671     GNUNET_CRYPTO_random_block (
    672       &puc->payment_identifier,
    673       sizeof (struct ANASTASIS_PaymentSecretP));
    674     puc->payment_identifier_provided = true;
    675     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    676                 "No payment identifier, initiating payment\n");
    677     return begin_payment (puc);
    678   }
    679   await_payment (puc);
    680   return MHD_YES;
    681 }
    682 
    683 
    684 enum MHD_Result
    685 AH_handler_policy_post (
    686   struct MHD_Connection *connection,
    687   struct TM_HandlerContext *hc,
    688   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub,
    689   const char *recovery_data,
    690   size_t *recovery_data_size)
    691 {
    692   struct PolicyUploadContext *puc = hc->ctx;
    693 
    694   if (NULL == puc)
    695   {
    696     /* first call, setup internals */
    697     puc = GNUNET_new (struct PolicyUploadContext);
    698     hc->ctx = puc;
    699     hc->cc = &cleanup_ctx;
    700     puc->con = connection;
    701 
    702     TALER_MHD_parse_request_header_auto (connection,
    703                                          ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER,
    704                                          &puc->payment_identifier,
    705                                          puc->payment_identifier_provided);
    706     puc->account = *account_pub;
    707 
    708     /* check for meta-data */
    709     {
    710       const char *metas;
    711 
    712       metas = MHD_lookup_connection_value (connection,
    713                                            MHD_HEADER_KIND,
    714                                            ANASTASIS_HTTP_HEADER_POLICY_META_DATA);
    715       if (NULL == metas)
    716       {
    717         GNUNET_break_op (0);
    718         return TALER_MHD_reply_with_error (
    719           connection,
    720           MHD_HTTP_BAD_REQUEST,
    721           TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    722           ANASTASIS_HTTP_HEADER_POLICY_META_DATA
    723           " header must be present");
    724       }
    725       if (GNUNET_OK !=
    726           GNUNET_STRINGS_string_to_data_alloc (metas,
    727                                                strlen (metas),
    728                                                &puc->meta_data,
    729                                                &puc->meta_data_size))
    730       {
    731         GNUNET_break_op (0);
    732         return TALER_MHD_reply_with_error (
    733           connection,
    734           MHD_HTTP_BAD_REQUEST,
    735           TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    736           ANASTASIS_HTTP_HEADER_POLICY_META_DATA
    737           " header must include a base32-encoded value");
    738       }
    739     }
    740     TALER_MHD_parse_request_header_auto_t (connection,
    741                                            ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE,
    742                                            &puc->account_sig);
    743     {
    744       /* Check if header contains an ETAG */
    745       const char *etag;
    746 
    747       etag = MHD_lookup_connection_value (connection,
    748                                           MHD_HEADER_KIND,
    749                                           MHD_HTTP_HEADER_IF_NONE_MATCH);
    750       if ( (NULL == etag) ||
    751            (2 >= strlen (etag)) ||
    752            ('"' != etag[0]) ||
    753            ('"' != etag[strlen (etag) - 1]) ||
    754            (GNUNET_OK !=
    755             GNUNET_STRINGS_string_to_data (etag + 1,
    756                                            strlen (etag) - 2,
    757                                            &puc->new_policy_upload_hash,
    758                                            sizeof (puc->new_policy_upload_hash))
    759            ) )
    760       {
    761         GNUNET_break_op (0);
    762         return TALER_MHD_reply_with_error (connection,
    763                                            MHD_HTTP_BAD_REQUEST,
    764                                            TALER_EC_ANASTASIS_POLICY_BAD_IF_MATCH,
    765                                            MHD_HTTP_HEADER_IF_NONE_MATCH
    766                                            " header must include a base32-encoded SHA-512 hash");
    767       }
    768     }
    769     /* validate signature */
    770     {
    771       struct ANASTASIS_UploadSignaturePS usp = {
    772         .purpose.size = htonl (sizeof (usp)),
    773         .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD),
    774         .new_recovery_data_hash = puc->new_policy_upload_hash
    775       };
    776 
    777       if (GNUNET_OK !=
    778           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD,
    779                                       &usp,
    780                                       &puc->account_sig.eddsa_sig,
    781                                       &account_pub->pub))
    782       {
    783         GNUNET_break_op (0);
    784         return TALER_MHD_reply_with_error (connection,
    785                                            MHD_HTTP_FORBIDDEN,
    786                                            TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE,
    787                                            ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE);
    788       }
    789     }
    790 
    791     /* Only now, with the account signature verified, do we allocate the
    792        upload buffer: sizing it from the client-declared Content-Length before
    793        checking who is asking lets anyone force repeated multi-megabyte
    794        allocations. */
    795     /* now setup 'puc' */
    796     {
    797       const char *lens;
    798       unsigned long len;
    799       char dummy;
    800 
    801       lens = MHD_lookup_connection_value (connection,
    802                                           MHD_HEADER_KIND,
    803                                           MHD_HTTP_HEADER_CONTENT_LENGTH);
    804       if ( (NULL == lens) ||
    805            (1 != sscanf (lens,
    806                          "%lu%c",
    807                          &len,
    808                          &dummy)) )
    809       {
    810         GNUNET_break_op (0);
    811         return TALER_MHD_reply_with_error (
    812           connection,
    813           MHD_HTTP_BAD_REQUEST,
    814           (NULL == lens)
    815           ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH
    816           : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH,
    817           NULL);
    818       }
    819       if (len / 1024 / 1024 >= AH_upload_limit_mb)
    820       {
    821         GNUNET_break_op (0);
    822         return TALER_MHD_reply_with_error (connection,
    823                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
    824                                            TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH,
    825                                            "Content-length value not acceptable");
    826       }
    827       puc->upload = GNUNET_malloc_large (len);
    828       if (NULL == puc->upload)
    829       {
    830         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    831                              "malloc");
    832         return TALER_MHD_reply_with_error (connection,
    833                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
    834                                            TALER_EC_ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH,
    835                                            NULL);
    836       }
    837       puc->upload_size = (size_t) len;
    838     }
    839 
    840     puc->timeout = GNUNET_TIME_relative_to_absolute (
    841       CHECK_PAYMENT_GENERIC_TIMEOUT);
    842     TALER_MHD_parse_request_timeout (connection,
    843                                      &puc->timeout);
    844 
    845     /* check if the client insists on paying */
    846     {
    847       const char *req;
    848       unsigned int years;
    849 
    850       req = MHD_lookup_connection_value (connection,
    851                                          MHD_GET_ARGUMENT_KIND,
    852                                          "storage_duration");
    853       if (NULL != req)
    854       {
    855         char dummy;
    856 
    857         if (1 != sscanf (req,
    858                          "%u%c",
    859                          &years,
    860                          &dummy))
    861         {
    862           GNUNET_break_op (0);
    863           return TALER_MHD_reply_with_error (connection,
    864                                              MHD_HTTP_BAD_REQUEST,
    865                                              TALER_EC_GENERIC_PARAMETER_MALFORMED,
    866                                              "storage_duration (must be non-negative number)");
    867         }
    868         if (years > ANASTASIS_MAX_YEARS_STORAGE)
    869         {
    870           GNUNET_break_op (0);
    871           return TALER_MHD_reply_with_error (
    872             connection,
    873             MHD_HTTP_BAD_REQUEST,
    874             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    875             "storage_duration (exceeds maximum storage duration)");
    876         }
    877       }
    878       else
    879       {
    880         years = 1;
    881       }
    882       puc->end_date = GNUNET_TIME_relative_to_timestamp (
    883         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    884                                        years));
    885     }
    886 
    887     /* get ready to hash (done here as we may go async for payments next) */
    888     puc->hash_ctx = GNUNET_CRYPTO_hash_context_start ();
    889 
    890     /* Check database to see if the transaction is permissible */
    891     {
    892       struct GNUNET_TIME_Relative rem;
    893 
    894       rem = GNUNET_TIME_absolute_get_remaining (puc->end_date.abs_time);
    895       puc->years_to_pay = rem.rel_value_us
    896                           / GNUNET_TIME_UNIT_YEARS.rel_value_us;
    897       if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us))
    898         puc->years_to_pay++;
    899       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    900                   "Calculated years to pay to be %u until %s\n",
    901                   puc->years_to_pay,
    902                   GNUNET_TIME_absolute2s (puc->end_date.abs_time));
    903 
    904       if (puc->payment_identifier_provided)
    905       {
    906         /* check if payment identifier is valid (existing and paid) */
    907         bool paid = false;
    908         bool valid_counter = false;
    909         enum GNUNET_DB_QueryStatus qs;
    910 
    911         qs = ANASTASIS_DB_get_recdoc_payment (
    912           &puc->account,
    913           &puc->payment_identifier,
    914           &paid,
    915           &valid_counter,
    916           &puc->existing_pi_timestamp);
    917         if (qs < 0)
    918           return TALER_MHD_reply_with_error (puc->con,
    919                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    920                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    921                                              NULL);
    922         if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    923         {
    924           /* The client named a payment identifier we have no record of.  The
    925              result specification was not touched in that case, so we must not
    926              look at @e paid or @e valid_counter; treat it as unpaid. */
    927           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    928                       "Given payment identifier is unknown, initiating payment\n");
    929           puc->payment_identifier_provided = false;
    930           if (0 == puc->years_to_pay)
    931             puc->years_to_pay = 1;
    932           return prepare_payment (puc);
    933         }
    934 
    935         if ( (! paid) ||
    936              (! valid_counter) )
    937         {
    938           if (! valid_counter)
    939           {
    940             puc->payment_identifier_provided = false;
    941             if (0 == puc->years_to_pay)
    942               puc->years_to_pay = 1;
    943             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    944                         "Too many uploads with this payment identifier, initiating fresh payment\n")
    945             ;
    946           }
    947           else
    948           {
    949             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    950                         "Given payment identifier not known to be paid, initiating payment\n");
    951           }
    952           return prepare_payment (puc);
    953         }
    954       }
    955 
    956       if (! puc->payment_identifier_provided)
    957       {
    958         enum GNUNET_DB_QueryStatus qs;
    959         struct GNUNET_TIME_Relative rel;
    960 
    961         /* generate fresh payment identifier */
    962         GNUNET_CRYPTO_random_block (&puc->payment_identifier,
    963                                     sizeof (struct ANASTASIS_PaymentSecretP));
    964         /* GNUNET_SYSERR is unreachable: a price list that mixes free and
    965            non-free currencies makes anastasis-httpd refuse to start. */
    966         GNUNET_assert (GNUNET_SYSERR !=
    967                        TALER_amount_list_check_uniform (&AH_annual_fees));
    968         if (GNUNET_NO !=
    969             TALER_amount_list_check_uniform (&AH_annual_fees))
    970         {
    971           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    972                       "No payment identifier, requesting payment\n");
    973           return begin_payment (puc);
    974         }
    975         /* Cost is zero, fake "zero" payment having happened */
    976         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    977                     "Policy upload is free, allowing upload without payment\n");
    978         qs = ANASTASIS_DB_do_insert_recdoc_payment (
    979           account_pub,
    980           AH_post_counter,
    981           &puc->payment_identifier,
    982           AH_primary_price (&AH_annual_fees));
    983         if (qs <= 0)
    984           return TALER_MHD_reply_with_error (puc->con,
    985                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    986                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    987                                              NULL);
    988         rel = GNUNET_TIME_relative_multiply (
    989           GNUNET_TIME_UNIT_YEARS,
    990           ANASTASIS_MAX_YEARS_STORAGE);
    991         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    992                     "Policy lifetime is %s (%u years)\n",
    993                     GNUNET_TIME_relative2s (rel,
    994                                             true),
    995                     ANASTASIS_MAX_YEARS_STORAGE);
    996         qs = ANASTASIS_DB_do_update_account_lifetime (
    997           account_pub,
    998           &puc->payment_identifier,
    999           GNUNET_TIME_relative_to_timestamp (rel),
   1000           GNUNET_TIME_UNIT_ZERO,
   1001           &puc->paid_until);
   1002         if (qs <= 0)
   1003         {
   1004           GNUNET_break (0);
   1005           return TALER_MHD_reply_with_error (puc->con,
   1006                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
   1007                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
   1008                                              NULL);
   1009         }
   1010       }
   1011     }
   1012 
   1013     /* Check if existing policy matches upload (and if, skip it) */
   1014     {
   1015       struct GNUNET_HashCode hash;
   1016       enum ANASTASIS_DB_AccountStatus as;
   1017       uint32_t version;
   1018       struct GNUNET_TIME_Timestamp now;
   1019       struct GNUNET_TIME_Relative rem;
   1020 
   1021       as = ANASTASIS_DB_get_account (
   1022         account_pub,
   1023         &puc->paid_until,
   1024         &hash,
   1025         &version);
   1026       now = GNUNET_TIME_timestamp_get ();
   1027       if (GNUNET_TIME_timestamp_cmp (puc->paid_until,
   1028                                      <,
   1029                                      now))
   1030         puc->paid_until = now;
   1031       rem = GNUNET_TIME_absolute_get_difference (puc->paid_until.abs_time,
   1032                                                  puc->end_date.abs_time);
   1033       puc->years_to_pay = rem.rel_value_us
   1034                           / GNUNET_TIME_UNIT_YEARS.rel_value_us;
   1035       if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us))
   1036         puc->years_to_pay++;
   1037       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1038                   "Calculated years to pay to be %u until %s\n",
   1039                   puc->years_to_pay,
   1040                   GNUNET_TIME_absolute2s (puc->end_date.abs_time));
   1041       if ( (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED == as) &&
   1042            (0 != puc->years_to_pay) )
   1043       {
   1044         /* user requested extension, force payment */
   1045         as = ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED;
   1046       }
   1047       switch (as)
   1048       {
   1049       case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED:
   1050         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1051                     "Expiration too low, initiating payment\n");
   1052         return prepare_payment (puc);
   1053       case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR:
   1054         return TALER_MHD_reply_with_error (puc->con,
   1055                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
   1056                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
   1057                                            NULL);
   1058       case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS:
   1059         /* continue below */
   1060         break;
   1061       case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED:
   1062         if (0 == GNUNET_memcmp (&hash,
   1063                                 &puc->new_policy_upload_hash))
   1064         {
   1065           /* Refuse upload: we already have that backup! */
   1066           struct MHD_Response *resp;
   1067           enum MHD_Result ret;
   1068           char version_s[14];
   1069 
   1070           GNUNET_snprintf (version_s,
   1071                            sizeof (version_s),
   1072                            "%u",
   1073                            (unsigned int) version);
   1074           resp = MHD_create_response_from_buffer (0,
   1075                                                   NULL,
   1076                                                   MHD_RESPMEM_PERSISTENT);
   1077           TALER_MHD_add_global_headers (resp,
   1078                                         false);
   1079           GNUNET_break (MHD_YES ==
   1080                         MHD_add_response_header (resp,
   1081                                                  ANASTASIS_HTTP_HEADER_POLICY_VERSION,
   1082                                                  version_s));
   1083           ret = MHD_queue_response (connection,
   1084                                     MHD_HTTP_NOT_MODIFIED,
   1085                                     resp);
   1086           GNUNET_break (MHD_YES == ret);
   1087           MHD_destroy_response (resp);
   1088           return ret;
   1089         }
   1090         break;
   1091       }
   1092     }
   1093     /* ready to begin! */
   1094     return MHD_YES;
   1095   }
   1096 
   1097   if (NULL != puc->resp)
   1098   {
   1099     enum MHD_Result ret;
   1100 
   1101     /* We generated a response asynchronously, queue that */
   1102     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1103                 "Returning asynchronously generated response with HTTP status %u\n",
   1104                 puc->response_code);
   1105     ret = MHD_queue_response (connection,
   1106                               puc->response_code,
   1107                               puc->resp);
   1108     GNUNET_break (MHD_YES == ret);
   1109     MHD_destroy_response (puc->resp);
   1110     puc->resp = NULL;
   1111     return ret;
   1112   }
   1113 
   1114   /* handle upload */
   1115   if (0 != *recovery_data_size)
   1116   {
   1117     /* This is NOT an MHD invariant we may assume: with the default client
   1118        discipline MHD accepts a request carrying both a "Content-Length" and
   1119        "Transfer-Encoding: chunked", ignores the former for framing (but keeps
   1120        it retrievable, which is where puc->upload_size came from) and then
   1121        hands us however many bytes the chunked body actually contains.  So a
   1122        client can send more than it declared; refuse it rather than write past
   1123        the end of the buffer. */
   1124     if (puc->upload_off + *recovery_data_size > puc->upload_size)
   1125     {
   1126       GNUNET_break_op (0);
   1127       return TALER_MHD_reply_with_error (
   1128         connection,
   1129         MHD_HTTP_BAD_REQUEST,
   1130         TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH,
   1131         "upload is larger than the declared content length");
   1132     }
   1133     memcpy (&puc->upload[puc->upload_off],
   1134             recovery_data,
   1135             *recovery_data_size);
   1136     puc->upload_off += *recovery_data_size;
   1137     GNUNET_CRYPTO_hash_context_read (puc->hash_ctx,
   1138                                      recovery_data,
   1139                                      *recovery_data_size);
   1140     *recovery_data_size = 0;
   1141     return MHD_YES;
   1142   }
   1143 
   1144   if ( (0 == puc->upload_off) &&
   1145        (0 != puc->upload_size) &&
   1146        (NULL == puc->resp) )
   1147   {
   1148     /* wait for upload */
   1149     return MHD_YES;
   1150   }
   1151 
   1152   /* finished with upload, check hash */
   1153   if (NULL != puc->hash_ctx)
   1154   {
   1155     struct GNUNET_HashCode our_hash;
   1156 
   1157     GNUNET_CRYPTO_hash_context_finish (puc->hash_ctx,
   1158                                        &our_hash);
   1159     puc->hash_ctx = NULL;
   1160     if (0 != GNUNET_memcmp (&our_hash,
   1161                             &puc->new_policy_upload_hash))
   1162     {
   1163       GNUNET_break_op (0);
   1164       return TALER_MHD_reply_with_error (connection,
   1165                                          MHD_HTTP_BAD_REQUEST,
   1166                                          TALER_EC_ANASTASIS_POLICY_INVALID_UPLOAD,
   1167                                          "Data uploaded does not match Etag promise");
   1168     }
   1169   }
   1170 
   1171   /* store backup to database */
   1172   {
   1173     enum ANASTASIS_DB_StoreStatus ss;
   1174     uint32_t version = UINT32_MAX;
   1175     char version_s[14];
   1176     char expir_s[32];
   1177 
   1178     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1179                 "Uploading recovery document\n");
   1180     ss = ANASTASIS_DB_do_insert_recovery_document (
   1181       &puc->account,
   1182       &puc->account_sig,
   1183       &puc->new_policy_upload_hash,
   1184       puc->upload,
   1185       puc->upload_size,
   1186       puc->meta_data,
   1187       puc->meta_data_size,
   1188       &puc->payment_identifier,
   1189       &version);
   1190     GNUNET_snprintf (version_s,
   1191                      sizeof (version_s),
   1192                      "%u",
   1193                      (unsigned int) version);
   1194     GNUNET_snprintf (expir_s,
   1195                      sizeof (expir_s),
   1196                      "%llu",
   1197                      (unsigned long long)
   1198                      (puc->paid_until.abs_time.abs_value_us
   1199                       / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
   1200     switch (ss)
   1201     {
   1202     case ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED:
   1203       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1204                   "Storage request limit exceeded, requesting payment\n");
   1205       /* This implies that a NEW payment is required, so the order we are
   1206          about to create must have a fresh ID.  Re-using the identifier the
   1207          client supplied would ask the merchant for an order that already
   1208          exists, which would not be helpful. */
   1209       GNUNET_CRYPTO_random_block (&puc->payment_identifier,
   1210                                   sizeof (struct ANASTASIS_PaymentSecretP));
   1211       puc->payment_identifier_provided = true;
   1212       return begin_payment (puc);
   1213     case ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED:
   1214       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1215                   "Policy store operation requires payment\n");
   1216       /* This again implies that a NEW payment is required, so the order we are
   1217          about to create must have a fresh ID.  Re-using the identifier the
   1218          client supplied would ask the merchant for an order that already
   1219          exists, which would not be helpful. */
   1220       GNUNET_CRYPTO_random_block (&puc->payment_identifier,
   1221                                   sizeof (struct ANASTASIS_PaymentSecretP));
   1222       puc->payment_identifier_provided = true;
   1223       return begin_payment (puc);
   1224     case ANASTASIS_DB_STORE_STATUS_HARD_ERROR:
   1225     case ANASTASIS_DB_STORE_STATUS_SOFT_ERROR:
   1226       return TALER_MHD_reply_with_error (puc->con,
   1227                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
   1228                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
   1229                                          NULL);
   1230     case ANASTASIS_DB_STORE_STATUS_NO_RESULTS:
   1231       {
   1232         /* database says nothing actually changed, 304 (could
   1233            theoretically happen if another equivalent upload succeeded
   1234            since we last checked!) */
   1235         struct MHD_Response *resp;
   1236         enum MHD_Result ret;
   1237 
   1238         resp = MHD_create_response_from_buffer (0,
   1239                                                 NULL,
   1240                                                 MHD_RESPMEM_PERSISTENT);
   1241         TALER_MHD_add_global_headers (resp,
   1242                                       false);
   1243         GNUNET_break (MHD_YES ==
   1244                       MHD_add_response_header (resp,
   1245                                                "Anastasis-Version",
   1246                                                version_s));
   1247         ret = MHD_queue_response (connection,
   1248                                   MHD_HTTP_NOT_MODIFIED,
   1249                                   resp);
   1250         GNUNET_break (MHD_YES == ret);
   1251         MHD_destroy_response (resp);
   1252         return ret;
   1253       }
   1254     case ANASTASIS_DB_STORE_STATUS_SUCCESS:
   1255       /* generate main (204) standard success reply */
   1256       {
   1257         struct MHD_Response *resp;
   1258         enum MHD_Result ret;
   1259 
   1260         resp = MHD_create_response_from_buffer (0,
   1261                                                 NULL,
   1262                                                 MHD_RESPMEM_PERSISTENT);
   1263         TALER_MHD_add_global_headers (resp,
   1264                                       false);
   1265         GNUNET_break (MHD_YES ==
   1266                       MHD_add_response_header (resp,
   1267                                                ANASTASIS_HTTP_HEADER_POLICY_VERSION,
   1268                                                version_s));
   1269         GNUNET_break (MHD_YES ==
   1270                       MHD_add_response_header (resp,
   1271                                                ANASTASIS_HTTP_HEADER_POLICY_EXPIRATION,
   1272                                                expir_s));
   1273         ret = MHD_queue_response (connection,
   1274                                   MHD_HTTP_NO_CONTENT,
   1275                                   resp);
   1276         GNUNET_break (MHD_YES == ret);
   1277         MHD_destroy_response (resp);
   1278         return ret;
   1279       }
   1280     }
   1281   }
   1282   GNUNET_break (0);
   1283   return MHD_NO;
   1284 }