LCOV - code coverage report
Current view: top level - sync - sync-httpd_backup_post.c (source / functions) Hit Total Coverage
Test: GNU Taler sync coverage report Lines: 0 304 0.0 %
Date: 2021-05-09 06:21:04 Functions: 0 10 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2019 Taler Systems SA
       4             : 
       5             :   TALER 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             :   TALER 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             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file sync-httpd_backup_post.c
      18             :  * @brief functions to handle incoming requests for backups
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include "sync-httpd.h"
      23             : #include <gnunet/gnunet_util_lib.h>
      24             : #include "sync-httpd_backup.h"
      25             : #include <taler/taler_json_lib.h>
      26             : #include <taler/taler_merchant_service.h>
      27             : #include <taler/taler_signatures.h>
      28             : 
      29             : 
      30             : /**
      31             :  * How long do we hold an HTTP client connection if
      32             :  * we are awaiting payment before giving up?
      33             :  */
      34             : #define CHECK_PAYMENT_GENERIC_TIMEOUT GNUNET_TIME_relative_multiply ( \
      35             :     GNUNET_TIME_UNIT_MINUTES, 30)
      36             : 
      37             : 
      38             : /**
      39             :  * Context for an upload operation.
      40             :  */
      41             : struct BackupContext
      42             : {
      43             : 
      44             :   /**
      45             :    * Context for cleanup logic.
      46             :    */
      47             :   struct TM_HandlerContext hc;
      48             : 
      49             :   /**
      50             :    * Signature of the account holder.
      51             :    */
      52             :   struct SYNC_AccountSignatureP account_sig;
      53             : 
      54             :   /**
      55             :    * Public key of the account holder.
      56             :    */
      57             :   struct SYNC_AccountPublicKeyP account;
      58             : 
      59             :   /**
      60             :    * Hash of the previous upload, or zeros if first upload.
      61             :    */
      62             :   struct GNUNET_HashCode old_backup_hash;
      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_backup_hash;
      69             : 
      70             :   /**
      71             :    * Claim token, all zeros if not known. Only set if @e existing_order_id is non-NULL.
      72             :    */
      73             :   struct TALER_ClaimTokenP token;
      74             : 
      75             :   /**
      76             :    * Hash context for the upload.
      77             :    */
      78             :   struct GNUNET_HashContext *hash_ctx;
      79             : 
      80             :   /**
      81             :    * Kept in DLL for shutdown handling while suspended.
      82             :    */
      83             :   struct BackupContext *next;
      84             : 
      85             :   /**
      86             :    * Kept in DLL for shutdown handling while suspended.
      87             :    */
      88             :   struct BackupContext *prev;
      89             : 
      90             :   /**
      91             :    * Used while suspended for resumption.
      92             :    */
      93             :   struct MHD_Connection *con;
      94             : 
      95             :   /**
      96             :    * Upload, with as many bytes as we have received so far.
      97             :    */
      98             :   char *upload;
      99             : 
     100             :   /**
     101             :    * Used while we are awaiting proposal creation.
     102             :    */
     103             :   struct TALER_MERCHANT_PostOrdersHandle *po;
     104             : 
     105             :   /**
     106             :    * Used while we are waiting payment.
     107             :    */
     108             :   struct TALER_MERCHANT_OrderMerchantGetHandle *omgh;
     109             : 
     110             :   /**
     111             :    * HTTP response code to use on resume, if non-NULL.
     112             : 
     113             :    */
     114             :   struct MHD_Response *resp;
     115             : 
     116             :   /**
     117             :    * Order under which the client promised payment, or NULL.
     118             :    */
     119             :   const char *order_id;
     120             : 
     121             :   /**
     122             :    * Order ID for the client that we found in our database.
     123             :    */
     124             :   char *existing_order_id;
     125             : 
     126             :   /**
     127             :    * Timestamp of the order in @e existing_order_id. Used to
     128             :    * select the most recent unpaid offer.
     129             :    */
     130             :   struct GNUNET_TIME_Absolute existing_order_timestamp;
     131             : 
     132             :   /**
     133             :    * Expected total upload size.
     134             :    */
     135             :   size_t upload_size;
     136             : 
     137             :   /**
     138             :    * Current offset for the upload.
     139             :    */
     140             :   size_t upload_off;
     141             : 
     142             :   /**
     143             :    * HTTP response code to use on resume, if resp is set.
     144             :    */
     145             :   unsigned int response_code;
     146             : 
     147             :   /**
     148             :    * Do not look for an existing order, force a fresh order to be created.
     149             :    */
     150             :   bool force_fresh_order;
     151             : };
     152             : 
     153             : 
     154             : /**
     155             :  * Kept in DLL for shutdown handling while suspended.
     156             :  */
     157             : static struct BackupContext *bc_head;
     158             : 
     159             : /**
     160             :  * Kept in DLL for shutdown handling while suspended.
     161             :  */
     162             : static struct BackupContext *bc_tail;
     163             : 
     164             : 
     165             : /**
     166             :  * Service is shutting down, resume all MHD connections NOW.
     167             :  */
     168             : void
     169           0 : SH_resume_all_bc ()
     170             : {
     171             :   struct BackupContext *bc;
     172             : 
     173           0 :   while (NULL != (bc = bc_head))
     174             :   {
     175           0 :     GNUNET_CONTAINER_DLL_remove (bc_head,
     176             :                                  bc_tail,
     177             :                                  bc);
     178           0 :     MHD_resume_connection (bc->con);
     179           0 :     if (NULL != bc->po)
     180             :     {
     181           0 :       TALER_MERCHANT_orders_post_cancel (bc->po);
     182           0 :       bc->po = NULL;
     183             :     }
     184           0 :     if (NULL != bc->omgh)
     185             :     {
     186           0 :       TALER_MERCHANT_merchant_order_get_cancel (bc->omgh);
     187           0 :       bc->omgh = NULL;
     188             :     }
     189             :   }
     190           0 : }
     191             : 
     192             : 
     193             : /**
     194             :  * Function called to clean up a backup context.
     195             :  *
     196             :  * @param hc a `struct BackupContext`
     197             :  */
     198             : static void
     199           0 : cleanup_ctx (struct TM_HandlerContext *hc)
     200             : {
     201           0 :   struct BackupContext *bc = (struct BackupContext *) hc;
     202             : 
     203           0 :   if (NULL != bc->po)
     204           0 :     TALER_MERCHANT_orders_post_cancel (bc->po);
     205           0 :   if (NULL != bc->hash_ctx)
     206           0 :     GNUNET_CRYPTO_hash_context_abort (bc->hash_ctx);
     207           0 :   if (NULL != bc->resp)
     208           0 :     MHD_destroy_response (bc->resp);
     209           0 :   GNUNET_free (bc->existing_order_id);
     210           0 :   GNUNET_free (bc->upload);
     211           0 :   GNUNET_free (bc);
     212           0 : }
     213             : 
     214             : 
     215             : /**
     216             :  * Transmit a payment request for @a order_id on @a connection
     217             :  *
     218             :  * @param connection MHD connection
     219             :  * @param order_id our backend's order ID
     220             :  * @param token the claim token generated by the merchant (NULL if
     221             :  *        it wasn't generated).
     222             :  * @return MHD response to use
     223             :  */
     224             : static struct MHD_Response *
     225           0 : make_payment_request (const char *order_id,
     226             :                       const struct TALER_ClaimTokenP *token)
     227             : {
     228             :   struct MHD_Response *resp;
     229             : 
     230             :   /* request payment via Taler */
     231           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     232             :               "Creating payment request for order `%s'\n",
     233             :               order_id);
     234           0 :   resp = MHD_create_response_from_buffer (0,
     235             :                                           NULL,
     236             :                                           MHD_RESPMEM_PERSISTENT);
     237           0 :   TALER_MHD_add_global_headers (resp);
     238             :   {
     239             :     char *hdr;
     240             :     char *pfx;
     241             :     char *hn;
     242           0 :     struct GNUNET_Buffer hdr_buf = { 0 };
     243             : 
     244           0 :     if (0 == strncasecmp ("https://",
     245             :                           SH_backend_url,
     246             :                           strlen ("https://")))
     247             :     {
     248           0 :       pfx = "taler://";
     249           0 :       hn = &SH_backend_url[strlen ("https://")];
     250             :     }
     251           0 :     else if (0 == strncasecmp ("http://",
     252             :                                SH_backend_url,
     253             :                                strlen ("http://")))
     254             :     {
     255           0 :       pfx = "taler+http://";
     256           0 :       hn = &SH_backend_url[strlen ("http://")];
     257             :     }
     258             :     else
     259             :     {
     260           0 :       GNUNET_break (0);
     261           0 :       MHD_destroy_response (resp);
     262           0 :       return NULL;
     263             :     }
     264           0 :     if (0 == strlen (hn))
     265             :     {
     266           0 :       GNUNET_break (0);
     267           0 :       MHD_destroy_response (resp);
     268           0 :       return NULL;
     269             :     }
     270             : 
     271           0 :     GNUNET_buffer_write_str (&hdr_buf, pfx);
     272           0 :     GNUNET_buffer_write_str (&hdr_buf, "pay/");
     273           0 :     GNUNET_buffer_write_str (&hdr_buf, hn);
     274           0 :     GNUNET_buffer_write_path (&hdr_buf, order_id);
     275             :     /* No session ID */
     276           0 :     GNUNET_buffer_write_path (&hdr_buf, "");
     277           0 :     if (NULL != token)
     278             :     {
     279           0 :       GNUNET_buffer_write_str (&hdr_buf, "?c=");
     280           0 :       GNUNET_buffer_write_data_encoded (&hdr_buf, token, sizeof (*token));
     281             :     }
     282           0 :     hdr = GNUNET_buffer_reap_str (&hdr_buf);
     283           0 :     GNUNET_break (MHD_YES ==
     284             :                   MHD_add_response_header (resp,
     285             :                                            "Taler",
     286             :                                            hdr));
     287           0 :     GNUNET_free (hdr);
     288             :   }
     289           0 :   return resp;
     290             : }
     291             : 
     292             : 
     293             : /**
     294             :  * Callbacks of this type are used to serve the result of submitting a
     295             :  * /contract request to a merchant.
     296             :  *
     297             :  * @param cls our `struct BackupContext`
     298             :  * @param por response details
     299             :  */
     300             : static void
     301           0 : proposal_cb (void *cls,
     302             :              const struct TALER_MERCHANT_PostOrdersReply *por)
     303             : {
     304           0 :   struct BackupContext *bc = cls;
     305             :   enum SYNC_DB_QueryStatus qs;
     306             : 
     307           0 :   bc->po = NULL;
     308           0 :   GNUNET_CONTAINER_DLL_remove (bc_head,
     309             :                                bc_tail,
     310             :                                bc);
     311           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     312             :               "Resuming connection with order `%s'\n",
     313             :               bc->order_id);
     314           0 :   MHD_resume_connection (bc->con);
     315           0 :   SH_trigger_daemon ();
     316           0 :   if (MHD_HTTP_OK != por->hr.http_status)
     317             :   {
     318           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     319             :                 "Backend returned status %u/%u\n",
     320             :                 por->hr.http_status,
     321             :                 (unsigned int) por->hr.ec);
     322           0 :     GNUNET_break_op (0);
     323           0 :     bc->resp = TALER_MHD_make_json_pack (
     324             :       "{s:I, s:s, s:I, s:I, s:O?}",
     325             :       "code",
     326             :       (json_int_t) TALER_EC_SYNC_PAYMENT_CREATE_BACKEND_ERROR,
     327             :       "hint",
     328             :       "Failed to setup order with merchant backend",
     329             :       "backend-ec",
     330           0 :       (json_int_t) por->hr.ec,
     331             :       "backend-http-status",
     332           0 :       (json_int_t) por->hr.http_status,
     333             :       "backend-reply",
     334             :       por->hr.reply);
     335           0 :     GNUNET_assert (NULL != bc->resp);
     336           0 :     bc->response_code = MHD_HTTP_BAD_GATEWAY;
     337           0 :     return;
     338             :   }
     339           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     340             :               "Storing payment request for order `%s'\n",
     341             :               por->details.ok.order_id);
     342           0 :   qs = db->store_payment_TR (db->cls,
     343           0 :                              &bc->account,
     344             :                              por->details.ok.order_id,
     345             :                              por->details.ok.token,
     346             :                              &SH_annual_fee);
     347           0 :   if (0 >= qs)
     348             :   {
     349           0 :     GNUNET_break (0);
     350           0 :     bc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
     351             :                                      "Failed to persist payment request in sync database");
     352           0 :     GNUNET_assert (NULL != bc->resp);
     353           0 :     bc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     354           0 :     return;
     355             :   }
     356           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     357             :               "Obtained fresh order `%s'\n",
     358             :               por->details.ok.order_id);
     359           0 :   bc->resp = make_payment_request (por->details.ok.order_id,
     360             :                                    por->details.ok.token);
     361           0 :   GNUNET_assert (NULL != bc->resp);
     362           0 :   bc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
     363             : }
     364             : 
     365             : 
     366             : /**
     367             :  * Function called on all pending payments for the right
     368             :  * account.
     369             :  *
     370             :  * @param cls closure, our `struct BackupContext`
     371             :  * @param timestamp for how long have we been waiting
     372             :  * @param order_id order id in the backend
     373             :  * @param token claim token to use (or NULL for none)
     374             :  * @param amount how much is the order for
     375             :  */
     376             : static void
     377           0 : ongoing_payment_cb (void *cls,
     378             :                     struct GNUNET_TIME_Absolute timestamp,
     379             :                     const char *order_id,
     380             :                     const struct TALER_ClaimTokenP *token,
     381             :                     const struct TALER_Amount *amount)
     382             : {
     383           0 :   struct BackupContext *bc = cls;
     384             : 
     385             :   (void) amount;
     386           0 :   if (0 != TALER_amount_cmp (amount,
     387             :                              &SH_annual_fee))
     388           0 :     return; /* can't re-use, fees changed */
     389           0 :   if ( (NULL == bc->existing_order_id) ||
     390           0 :        (bc->existing_order_timestamp.abs_value_us < timestamp.abs_value_us) )
     391             :   {
     392           0 :     GNUNET_free (bc->existing_order_id);
     393           0 :     bc->existing_order_id = GNUNET_strdup (order_id);
     394           0 :     bc->existing_order_timestamp = timestamp;
     395           0 :     if (NULL != token)
     396           0 :       bc->token = *token;
     397             :   }
     398             : }
     399             : 
     400             : 
     401             : /**
     402             :  * Callback to process a GET /check-payment request
     403             :  *
     404             :  * @param cls our `struct BackupContext`
     405             :  * @param hr HTTP response details
     406             :  * @param osr order status
     407             :  */
     408             : static void
     409           0 : check_payment_cb (void *cls,
     410             :                   const struct TALER_MERCHANT_HttpResponse *hr,
     411             :                   const struct TALER_MERCHANT_OrderStatusResponse *osr)
     412             : {
     413           0 :   struct BackupContext *bc = cls;
     414             : 
     415             :   /* refunds are not supported, verify */
     416           0 :   bc->omgh = NULL;
     417           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     418             :               "Payment status checked: %d\n",
     419             :               osr->status);
     420           0 :   GNUNET_CONTAINER_DLL_remove (bc_head,
     421             :                                bc_tail,
     422             :                                bc);
     423           0 :   MHD_resume_connection (bc->con);
     424           0 :   SH_trigger_daemon ();
     425           0 :   switch (osr->status)
     426             :   {
     427           0 :   case TALER_MERCHANT_OSC_PAID:
     428             :     {
     429             :       enum SYNC_DB_QueryStatus qs;
     430             : 
     431           0 :       qs = db->increment_lifetime_TR (db->cls,
     432           0 :                                       &bc->account,
     433             :                                       bc->order_id,
     434             :                                       GNUNET_TIME_UNIT_YEARS); /* always annual */
     435           0 :       if (0 <= qs)
     436           0 :         return; /* continue as planned */
     437           0 :       GNUNET_break (0);
     438           0 :       bc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
     439             :                                        "increment lifetime");
     440           0 :       GNUNET_assert (NULL != bc->resp);
     441           0 :       bc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     442           0 :       return; /* continue as planned */
     443             :     }
     444           0 :   case TALER_MERCHANT_OSC_UNPAID:
     445             :   case TALER_MERCHANT_OSC_CLAIMED:
     446           0 :     break;
     447             :   }
     448           0 :   if (NULL != bc->existing_order_id)
     449             :   {
     450             :     /* repeat payment request */
     451           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     452             :                 "Repeating payment request\n");
     453           0 :     bc->resp = make_payment_request (bc->existing_order_id,
     454           0 :                                      (GNUNET_YES == GNUNET_is_zero (&bc->token))
     455             :                                      ? NULL
     456             :                                      : &bc->token);
     457           0 :     GNUNET_assert (NULL != bc->resp);
     458           0 :     bc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
     459           0 :     return;
     460             :   }
     461           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     462             :               "Timeout waiting for payment\n");
     463           0 :   bc->resp = TALER_MHD_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT,
     464             :                                    "Timeout awaiting promised payment");
     465           0 :   GNUNET_assert (NULL != bc->resp);
     466           0 :   bc->response_code = MHD_HTTP_REQUEST_TIMEOUT;
     467             : }
     468             : 
     469             : 
     470             : /**
     471             :  * Helper function used to ask our backend to await
     472             :  * a payment for the user's account.
     473             :  *
     474             :  * @param bc context to begin payment for.
     475             :  * @param timeout when to give up trying
     476             :  * @param order_id which order to check for the payment
     477             :  */
     478             : static void
     479           0 : await_payment (struct BackupContext *bc,
     480             :                struct GNUNET_TIME_Relative timeout,
     481             :                const char *order_id)
     482             : {
     483           0 :   GNUNET_CONTAINER_DLL_insert (bc_head,
     484             :                                bc_tail,
     485             :                                bc);
     486           0 :   MHD_suspend_connection (bc->con);
     487           0 :   bc->order_id = order_id;
     488           0 :   bc->omgh = TALER_MERCHANT_merchant_order_get (SH_ctx,
     489             :                                                 SH_backend_url,
     490             :                                                 order_id,
     491             :                                                 NULL /* our payments are NOT session-bound */,
     492             :                                                 false,
     493             :                                                 timeout,
     494             :                                                 &check_payment_cb,
     495             :                                                 bc);
     496           0 :   SH_trigger_curl ();
     497           0 : }
     498             : 
     499             : 
     500             : /**
     501             :  * Helper function used to ask our backend to begin
     502             :  * processing a payment for the user's account.
     503             :  * May perform asynchronous operations by suspending the connection
     504             :  * if required.
     505             :  *
     506             :  * @param bc context to begin payment for.
     507             :  * @param pay_req #GNUNET_YES if payment was explicitly requested,
     508             :  *                #GNUNET_NO if payment is needed
     509             :  * @return MHD status code
     510             :  */
     511             : static MHD_RESULT
     512           0 : begin_payment (struct BackupContext *bc,
     513             :                int pay_req)
     514             : {
     515             :   json_t *order;
     516             : 
     517           0 :   if (! bc->force_fresh_order)
     518             :   {
     519             :     enum GNUNET_DB_QueryStatus qs;
     520             : 
     521           0 :     qs = db->lookup_pending_payments_by_account_TR (db->cls,
     522           0 :                                                     &bc->account,
     523             :                                                     &ongoing_payment_cb,
     524             :                                                     bc);
     525           0 :     if (qs < 0)
     526             :     {
     527             :       struct MHD_Response *resp;
     528             :       MHD_RESULT ret;
     529             : 
     530           0 :       resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
     531             :                                    "pending payments");
     532           0 :       ret = MHD_queue_response (bc->con,
     533             :                                 MHD_HTTP_INTERNAL_SERVER_ERROR,
     534             :                                 resp);
     535           0 :       GNUNET_break (MHD_YES == ret);
     536           0 :       MHD_destroy_response (resp);
     537           0 :       return ret;
     538             :     }
     539           0 :     if (NULL != bc->existing_order_id)
     540             :     {
     541           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     542             :                   "Have existing order, waiting for `%s' to complete\n",
     543             :                   bc->existing_order_id);
     544           0 :       await_payment (bc,
     545             :                      GNUNET_TIME_UNIT_ZERO /* no long polling */,
     546           0 :                      bc->existing_order_id);
     547           0 :       return MHD_YES;
     548             :     }
     549             :   }
     550           0 :   GNUNET_CONTAINER_DLL_insert (bc_head,
     551             :                                bc_tail,
     552             :                                bc);
     553           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     554             :               "Suspending connection while creating order at `%s'\n",
     555             :               SH_backend_url);
     556           0 :   MHD_suspend_connection (bc->con);
     557           0 :   order = json_pack ("{s:o, s:s, s:s}",
     558             :                      "amount", TALER_JSON_from_amount (&SH_annual_fee),
     559             :                      "summary", "annual fee for sync service",
     560             :                      "fulfillment_url", SH_fulfillment_url);
     561           0 :   bc->po = TALER_MERCHANT_orders_post2 (SH_ctx,
     562             :                                         SH_backend_url,
     563             :                                         order,
     564             :                                         GNUNET_TIME_UNIT_ZERO,
     565             :                                         NULL, /* no payment target */
     566             :                                         0,
     567             :                                         NULL, /* no inventory products */
     568             :                                         0,
     569             :                                         NULL, /* no uuids */
     570             :                                         false, /* do NOT require claim token */
     571             :                                         &proposal_cb,
     572             :                                         bc);
     573           0 :   SH_trigger_curl ();
     574           0 :   json_decref (order);
     575           0 :   return MHD_YES;
     576             : }
     577             : 
     578             : 
     579             : /**
     580             :  * We got some query status from the DB.  Handle the error cases.
     581             :  * May perform asynchronous operations by suspending the connection
     582             :  * if required.
     583             :  *
     584             :  * @param bc connection to handle status for
     585             :  * @param qs query status to handle
     586             :  * @return #MHD_YES or #MHD_NO
     587             :  */
     588             : static MHD_RESULT
     589           0 : handle_database_error (struct BackupContext *bc,
     590             :                        enum SYNC_DB_QueryStatus qs)
     591             : {
     592           0 :   switch (qs)
     593             :   {
     594           0 :   case SYNC_DB_OLD_BACKUP_MISSING:
     595           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     596             :                 "Update failed: no existing backup\n");
     597           0 :     return TALER_MHD_reply_with_error (bc->con,
     598             :                                        MHD_HTTP_NOT_FOUND,
     599             :                                        TALER_EC_SYNC_PREVIOUS_BACKUP_UNKNOWN,
     600             :                                        NULL);
     601           0 :   case SYNC_DB_OLD_BACKUP_MISMATCH:
     602           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     603             :                 "Conflict detected, returning existing backup\n");
     604           0 :     return SH_return_backup (bc->con,
     605           0 :                              &bc->account,
     606             :                              MHD_HTTP_CONFLICT);
     607           0 :   case SYNC_DB_PAYMENT_REQUIRED:
     608             :     {
     609             :       const char *order_id;
     610             : 
     611           0 :       order_id = MHD_lookup_connection_value (bc->con,
     612             :                                               MHD_GET_ARGUMENT_KIND,
     613             :                                               "paying");
     614           0 :       if (NULL == order_id)
     615             :       {
     616           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     617             :                     "Payment required, starting payment process\n");
     618           0 :         return begin_payment (bc,
     619             :                               GNUNET_NO);
     620             :       }
     621           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     622             :                   "Payment required, awaiting completion of `%s'\n",
     623             :                   order_id);
     624           0 :       await_payment (bc,
     625             :                      CHECK_PAYMENT_GENERIC_TIMEOUT,
     626             :                      order_id);
     627             :     }
     628           0 :     return MHD_YES;
     629           0 :   case SYNC_DB_HARD_ERROR:
     630           0 :     GNUNET_break (0);
     631           0 :     return TALER_MHD_reply_with_error (bc->con,
     632             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     633             :                                        TALER_EC_GENERIC_DB_COMMIT_FAILED,
     634             :                                        NULL);
     635           0 :   case SYNC_DB_SOFT_ERROR:
     636           0 :     GNUNET_break (0);
     637           0 :     return TALER_MHD_reply_with_error (bc->con,
     638             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     639             :                                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
     640             :                                        NULL);
     641           0 :   case SYNC_DB_NO_RESULTS:
     642           0 :     GNUNET_assert (0);
     643             :     return MHD_NO;
     644             :   /* intentional fall-through! */
     645           0 :   case SYNC_DB_ONE_RESULT:
     646           0 :     GNUNET_assert (0);
     647             :     return MHD_NO;
     648             :   }
     649           0 :   GNUNET_break (0);
     650           0 :   return MHD_NO;
     651             : }
     652             : 
     653             : 
     654             : /**
     655             :  * Handle a client POSTing a backup to us.
     656             :  *
     657             :  * @param connection the MHD connection to handle
     658             :  * @param[in,out] connection_cls the connection's closure (can be updated)
     659             :  * @param account public key of the account the request is for
     660             :  * @param upload_data upload data
     661             :  * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
     662             :  * @return MHD result code
     663             :  */
     664             : MHD_RESULT
     665           0 : SH_backup_post (struct MHD_Connection *connection,
     666             :                 void **con_cls,
     667             :                 const struct SYNC_AccountPublicKeyP *account,
     668             :                 const char *upload_data,
     669             :                 size_t *upload_data_size)
     670             : {
     671             :   struct BackupContext *bc;
     672             : 
     673           0 :   bc = *con_cls;
     674           0 :   if (NULL == bc)
     675             :   {
     676             :     /* first call, setup internals */
     677           0 :     bc = GNUNET_new (struct BackupContext);
     678           0 :     bc->hc.cc = &cleanup_ctx;
     679           0 :     bc->con = connection;
     680           0 :     bc->account = *account;
     681             :     {
     682             :       const char *fresh;
     683             : 
     684           0 :       fresh = MHD_lookup_connection_value (connection,
     685             :                                            MHD_GET_ARGUMENT_KIND,
     686             :                                            "fresh");
     687           0 :       if (NULL != fresh)
     688           0 :         bc->force_fresh_order = true;
     689             :     }
     690           0 :     *con_cls = bc;
     691             : 
     692             :     /* now setup 'bc' */
     693             :     {
     694             :       const char *lens;
     695             :       unsigned long len;
     696             : 
     697           0 :       lens = MHD_lookup_connection_value (connection,
     698             :                                           MHD_HEADER_KIND,
     699             :                                           MHD_HTTP_HEADER_CONTENT_LENGTH);
     700           0 :       if ( (NULL == lens) ||
     701             :            (1 !=
     702           0 :             sscanf (lens,
     703             :                     "%lu",
     704             :                     &len)) )
     705             :       {
     706           0 :         GNUNET_break_op (0);
     707           0 :         return TALER_MHD_reply_with_error (
     708             :           connection,
     709             :           MHD_HTTP_BAD_REQUEST,
     710             :           (NULL == lens)
     711             :           ? TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH
     712             :           : TALER_EC_SYNC_MISSING_CONTENT_LENGTH,
     713             :           lens);
     714             :       }
     715           0 :       if (len / 1024 / 1024 >= SH_upload_limit_mb)
     716             :       {
     717           0 :         GNUNET_break_op (0);
     718           0 :         return TALER_MHD_reply_with_error (connection,
     719             :                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
     720             :                                            TALER_EC_SYNC_EXCESSIVE_CONTENT_LENGTH,
     721             :                                            NULL);
     722             :       }
     723           0 :       bc->upload = GNUNET_malloc_large (len);
     724           0 :       if (NULL == bc->upload)
     725             :       {
     726           0 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     727             :                              "malloc");
     728           0 :         return TALER_MHD_reply_with_error (connection,
     729             :                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
     730             :                                            TALER_EC_SYNC_OUT_OF_MEMORY_ON_CONTENT_LENGTH,
     731             :                                            NULL);
     732             :       }
     733           0 :       bc->upload_size = (size_t) len;
     734             :     }
     735             :     {
     736             :       const char *im;
     737             : 
     738           0 :       im = MHD_lookup_connection_value (connection,
     739             :                                         MHD_HEADER_KIND,
     740             :                                         MHD_HTTP_HEADER_IF_MATCH);
     741           0 :       if ( (NULL != im) &&
     742             :            (GNUNET_OK !=
     743           0 :             GNUNET_STRINGS_string_to_data (im,
     744             :                                            strlen (im),
     745           0 :                                            &bc->old_backup_hash,
     746             :                                            sizeof (bc->old_backup_hash))) )
     747             :       {
     748           0 :         GNUNET_break_op (0);
     749           0 :         return TALER_MHD_reply_with_error (connection,
     750             :                                            MHD_HTTP_BAD_REQUEST,
     751             :                                            TALER_EC_SYNC_BAD_IF_MATCH,
     752             :                                            NULL);
     753             :       }
     754             :     }
     755             :     {
     756             :       const char *sig_s;
     757             : 
     758           0 :       sig_s = MHD_lookup_connection_value (connection,
     759             :                                            MHD_HEADER_KIND,
     760             :                                            "Sync-Signature");
     761           0 :       if ( (NULL == sig_s) ||
     762             :            (GNUNET_OK !=
     763           0 :             GNUNET_STRINGS_string_to_data (sig_s,
     764             :                                            strlen (sig_s),
     765           0 :                                            &bc->account_sig,
     766             :                                            sizeof (bc->account_sig))) )
     767             :       {
     768           0 :         GNUNET_break_op (0);
     769           0 :         return TALER_MHD_reply_with_error (connection,
     770             :                                            MHD_HTTP_BAD_REQUEST,
     771             :                                            TALER_EC_SYNC_BAD_SYNC_SIGNATURE,
     772             :                                            NULL);
     773             :       }
     774             :     }
     775             :     {
     776             :       const char *etag;
     777             : 
     778           0 :       etag = MHD_lookup_connection_value (connection,
     779             :                                           MHD_HEADER_KIND,
     780             :                                           MHD_HTTP_HEADER_IF_NONE_MATCH);
     781           0 :       if ( (NULL == etag) ||
     782             :            (GNUNET_OK !=
     783           0 :             GNUNET_STRINGS_string_to_data (etag,
     784             :                                            strlen (etag),
     785           0 :                                            &bc->new_backup_hash,
     786             :                                            sizeof (bc->new_backup_hash))) )
     787             :       {
     788           0 :         GNUNET_break_op (0);
     789           0 :         return TALER_MHD_reply_with_error (connection,
     790             :                                            MHD_HTTP_BAD_REQUEST,
     791             :                                            TALER_EC_SYNC_BAD_IF_NONE_MATCH,
     792             :                                            NULL);
     793             :       }
     794             :     }
     795             :     /* validate signature */
     796             :     {
     797           0 :       struct SYNC_UploadSignaturePS usp = {
     798           0 :         .purpose.size = htonl (sizeof (usp)),
     799           0 :         .purpose.purpose = htonl (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD),
     800             :         .old_backup_hash = bc->old_backup_hash,
     801             :         .new_backup_hash = bc->new_backup_hash
     802             :       };
     803             : 
     804           0 :       if (GNUNET_OK !=
     805           0 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD,
     806             :                                       &usp,
     807             :                                       &bc->account_sig.eddsa_sig,
     808             :                                       &account->eddsa_pub))
     809             :       {
     810           0 :         GNUNET_break_op (0);
     811           0 :         return TALER_MHD_reply_with_error (connection,
     812             :                                            MHD_HTTP_FORBIDDEN,
     813             :                                            TALER_EC_SYNC_INVALID_SIGNATURE,
     814             :                                            NULL);
     815             :       }
     816             :     }
     817             :     /* get ready to hash (done here as we may go async for payments next) */
     818           0 :     bc->hash_ctx = GNUNET_CRYPTO_hash_context_start ();
     819             : 
     820             :     /* Check database to see if the transaction is permissible */
     821             :     {
     822             :       struct GNUNET_HashCode hc;
     823             :       enum SYNC_DB_QueryStatus qs;
     824             : 
     825           0 :       qs = db->lookup_account_TR (db->cls,
     826             :                                   account,
     827             :                                   &hc);
     828           0 :       if (qs < 0)
     829           0 :         return handle_database_error (bc,
     830             :                                       qs);
     831           0 :       if (SYNC_DB_NO_RESULTS == qs)
     832           0 :         memset (&hc, 0, sizeof (hc));
     833           0 :       if (0 == GNUNET_memcmp (&hc,
     834             :                               &bc->new_backup_hash))
     835             :       {
     836             :         /* Refuse upload: we already have that backup! */
     837             :         struct MHD_Response *resp;
     838             :         MHD_RESULT ret;
     839             : 
     840           0 :         resp = MHD_create_response_from_buffer (0,
     841             :                                                 NULL,
     842             :                                                 MHD_RESPMEM_PERSISTENT);
     843           0 :         TALER_MHD_add_global_headers (resp);
     844           0 :         ret = MHD_queue_response (connection,
     845             :                                   MHD_HTTP_NOT_MODIFIED,
     846             :                                   resp);
     847           0 :         GNUNET_break (MHD_YES == ret);
     848           0 :         MHD_destroy_response (resp);
     849           0 :         return ret;
     850             :       }
     851           0 :       if (0 != GNUNET_memcmp (&hc,
     852             :                               &bc->old_backup_hash))
     853             :       {
     854             :         /* Refuse upload: if-none-match failed! */
     855           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     856             :                     "Conflict detected, returning existing backup\n");
     857           0 :         return SH_return_backup (connection,
     858             :                                  account,
     859             :                                  MHD_HTTP_CONFLICT);
     860             :       }
     861             :     }
     862             :     /* check if the client insists on paying */
     863             :     {
     864             :       const char *order_req;
     865             : 
     866           0 :       order_req = MHD_lookup_connection_value (connection,
     867             :                                                MHD_GET_ARGUMENT_KIND,
     868             :                                                "pay");
     869           0 :       if (NULL != order_req)
     870             :       {
     871           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     872             :                     "Payment requested, starting payment process\n");
     873           0 :         return begin_payment (bc,
     874             :                               GNUNET_YES);
     875             :       }
     876             :     }
     877             :     /* ready to begin! */
     878           0 :     return MHD_YES;
     879             :   }
     880             :   /* handle upload */
     881           0 :   if (0 != *upload_data_size)
     882             :   {
     883             :     /* check MHD invariant */
     884           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     885             :                 "Processing %u bytes of upload data\n",
     886             :                 (unsigned int) *upload_data_size);
     887           0 :     GNUNET_assert (bc->upload_off + *upload_data_size <= bc->upload_size);
     888           0 :     memcpy (&bc->upload[bc->upload_off],
     889             :             upload_data,
     890             :             *upload_data_size);
     891           0 :     bc->upload_off += *upload_data_size;
     892           0 :     GNUNET_CRYPTO_hash_context_read (bc->hash_ctx,
     893             :                                      upload_data,
     894             :                                      *upload_data_size);
     895           0 :     *upload_data_size = 0;
     896           0 :     return MHD_YES;
     897             :   }
     898           0 :   else if ( (0 == bc->upload_off) &&
     899           0 :             (0 != bc->upload_size) &&
     900           0 :             (NULL == bc->resp) )
     901             :   {
     902             :     /* wait for upload */
     903           0 :     return MHD_YES;
     904             :   }
     905           0 :   if (NULL != bc->resp)
     906             :   {
     907             :     MHD_RESULT ret;
     908             : 
     909             :     /* We generated a response asynchronously, queue that */
     910           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     911             :                 "Returning asynchronously generated response with HTTP status %u\n",
     912             :                 bc->response_code);
     913           0 :     ret = MHD_queue_response (connection,
     914             :                               bc->response_code,
     915             :                               bc->resp);
     916           0 :     GNUNET_break (MHD_YES == ret);
     917           0 :     MHD_destroy_response (bc->resp);
     918           0 :     bc->resp = NULL;
     919           0 :     return ret;
     920             :   }
     921             : 
     922             :   /* finished with upload, check hash */
     923             :   {
     924             :     struct GNUNET_HashCode our_hash;
     925             : 
     926           0 :     GNUNET_CRYPTO_hash_context_finish (bc->hash_ctx,
     927             :                                        &our_hash);
     928           0 :     bc->hash_ctx = NULL;
     929           0 :     if (0 != GNUNET_memcmp (&our_hash,
     930             :                             &bc->new_backup_hash))
     931             :     {
     932           0 :       GNUNET_break_op (0);
     933           0 :       return TALER_MHD_reply_with_error (connection,
     934             :                                          MHD_HTTP_BAD_REQUEST,
     935             :                                          TALER_EC_SYNC_INVALID_UPLOAD,
     936             :                                          NULL);
     937             :     }
     938             :   }
     939             : 
     940             :   /* store backup to database */
     941             :   {
     942             :     enum SYNC_DB_QueryStatus qs;
     943             : 
     944           0 :     if (GNUNET_YES == GNUNET_is_zero (&bc->old_backup_hash))
     945             :     {
     946           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     947             :                   "Uploading first backup to account\n");
     948           0 :       qs = db->store_backup_TR (db->cls,
     949             :                                 account,
     950           0 :                                 &bc->account_sig,
     951           0 :                                 &bc->new_backup_hash,
     952             :                                 bc->upload_size,
     953           0 :                                 bc->upload);
     954             :     }
     955             :     else
     956             :     {
     957           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     958             :                   "Uploading existing backup of account\n");
     959           0 :       qs = db->update_backup_TR (db->cls,
     960             :                                  account,
     961           0 :                                  &bc->old_backup_hash,
     962           0 :                                  &bc->account_sig,
     963           0 :                                  &bc->new_backup_hash,
     964             :                                  bc->upload_size,
     965           0 :                                  bc->upload);
     966             :     }
     967           0 :     if (qs < 0)
     968           0 :       return handle_database_error (bc,
     969             :                                     qs);
     970           0 :     if (0 == qs)
     971             :     {
     972             :       /* database says nothing actually changed, 304 (could
     973             :          theoretically happen if another equivalent upload succeeded
     974             :          since we last checked!) */
     975             :       struct MHD_Response *resp;
     976             :       MHD_RESULT ret;
     977             : 
     978           0 :       resp = MHD_create_response_from_buffer (0,
     979             :                                               NULL,
     980             :                                               MHD_RESPMEM_PERSISTENT);
     981           0 :       TALER_MHD_add_global_headers (resp);
     982           0 :       ret = MHD_queue_response (connection,
     983             :                                 MHD_HTTP_NOT_MODIFIED,
     984             :                                 resp);
     985           0 :       GNUNET_break (MHD_YES == ret);
     986           0 :       MHD_destroy_response (resp);
     987           0 :       return ret;
     988             :     }
     989             :   }
     990             : 
     991             :   /* generate main (204) standard success reply */
     992             :   {
     993             :     struct MHD_Response *resp;
     994             :     MHD_RESULT ret;
     995             : 
     996           0 :     resp = MHD_create_response_from_buffer (0,
     997             :                                             NULL,
     998             :                                             MHD_RESPMEM_PERSISTENT);
     999           0 :     TALER_MHD_add_global_headers (resp);
    1000           0 :     ret = MHD_queue_response (connection,
    1001             :                               MHD_HTTP_NO_CONTENT,
    1002             :                               resp);
    1003           0 :     GNUNET_break (MHD_YES == ret);
    1004           0 :     MHD_destroy_response (resp);
    1005           0 :     return ret;
    1006             :   }
    1007             : }

Generated by: LCOV version 1.14