LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_reserves_history.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 76 181 42.0 %
Date: 2025-07-14 11:22:44 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2024 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 taler-exchange-httpd_reserves_history.c
      18             :  * @brief Handle /reserves/$RESERVE_PUB HISTORY requests
      19             :  * @author Florian Dold
      20             :  * @author Benedikt Mueller
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "taler/platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <jansson.h>
      26             : #include "taler/taler_json_lib.h"
      27             : #include "taler/taler_dbevents.h"
      28             : #include "taler-exchange-httpd_keys.h"
      29             : #include "taler-exchange-httpd_reserves_history.h"
      30             : #include "taler-exchange-httpd_responses.h"
      31             : 
      32             : 
      33             : /**
      34             :  * Compile the history of a reserve into a JSON object.
      35             :  *
      36             :  * @param rh reserve history to JSON-ify
      37             :  * @return json representation of the @a rh, NULL on error
      38             :  */
      39             : static json_t *
      40           8 : compile_reserve_history (
      41             :   const struct TALER_EXCHANGEDB_ReserveHistory *rh)
      42             : {
      43             :   json_t *json_history;
      44             : 
      45           8 :   json_history = json_array ();
      46           8 :   GNUNET_assert (NULL != json_history);
      47           8 :   for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
      48          31 :        NULL != pos;
      49          23 :        pos = pos->next)
      50             :   {
      51          23 :     switch (pos->type)
      52             :     {
      53           8 :     case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
      54             :       {
      55           8 :         const struct TALER_EXCHANGEDB_BankTransfer *bank =
      56             :           pos->details.bank;
      57             : 
      58           8 :         if (0 !=
      59           8 :             json_array_append_new (
      60             :               json_history,
      61           8 :               GNUNET_JSON_PACK (
      62             :                 GNUNET_JSON_pack_string ("type",
      63             :                                          "CREDIT"),
      64             :                 GNUNET_JSON_pack_timestamp ("timestamp",
      65             :                                             bank->execution_date),
      66             :                 TALER_JSON_pack_full_payto ("sender_account_url",
      67             :                                             bank->sender_account_details),
      68             :                 GNUNET_JSON_pack_uint64 ("wire_reference",
      69             :                                          bank->wire_reference),
      70             :                 TALER_JSON_pack_amount ("amount",
      71             :                                         &bank->amount))))
      72             :         {
      73           0 :           GNUNET_break (0);
      74           0 :           json_decref (json_history);
      75           0 :           return NULL;
      76             :         }
      77           8 :         break;
      78             :       }
      79           7 :     case TALER_EXCHANGEDB_RO_WITHDRAW_COINS:
      80             :       {
      81           7 :         const struct TALER_EXCHANGEDB_Withdraw *withdraw
      82             :           = pos->details.withdraw;
      83             :         struct TALER_Amount withdraw_fee;
      84             :         struct TEH_KeyStateHandle *ksh;
      85             : 
      86           7 :         GNUNET_assert (GNUNET_OK ==
      87             :                        TALER_amount_set_zero (
      88             :                          TEH_currency,
      89             :                          &withdraw_fee));
      90             : 
      91             :         /*
      92             :          * We need to calculate the fees for the withdraw.
      93             :          * Therefore, we need to get access to the key state.
      94             :          */
      95           7 :         ksh = TEH_keys_get_state ();
      96           7 :         if (NULL == ksh)
      97             :         {
      98           0 :           GNUNET_break (0);
      99           0 :           json_decref (json_history);
     100           0 :           return NULL;
     101             :         }
     102             : 
     103          16 :         for (size_t i = 0; i < withdraw->num_coins; i++)
     104             :         {
     105             :           /* Find the denomination and accumulate the fee */
     106             :           {
     107             :             struct TEH_DenominationKey *dk;
     108           9 :             dk = TEH_keys_denomination_by_hash_from_state (
     109             :               ksh,
     110           9 :               &withdraw->denom_pub_hashes[i],
     111             :               NULL,
     112             :               NULL);
     113             : 
     114           9 :             if (NULL == dk)
     115             :             {
     116           0 :               GNUNET_break (0);
     117           0 :               json_decref (json_history);
     118           0 :               return NULL;
     119             :             }
     120             : 
     121           9 :             if (0 > TALER_amount_add (&withdraw_fee,
     122             :                                       &withdraw_fee,
     123           9 :                                       &dk->meta.fees.withdraw))
     124             :             {
     125           0 :               GNUNET_break (0);
     126           0 :               json_decref (json_history);
     127           0 :               return NULL;
     128             :             }
     129             :           }
     130             :         }
     131             : 
     132             :         /* Prepare the entry for the history */
     133             :         {
     134           7 :           json_t *j_entry = GNUNET_JSON_PACK (
     135             :             GNUNET_JSON_pack_string (
     136             :               "type",
     137             :               "WITHDRAW"),
     138             :             GNUNET_JSON_pack_data_auto (
     139             :               "reserve_sig",
     140             :               &withdraw->reserve_sig),
     141             :             GNUNET_JSON_pack_data_auto (
     142             :               "planchets_h",
     143             :               &withdraw->planchets_h),
     144             :             GNUNET_JSON_pack_uint64 (
     145             :               "num_coins",
     146             :               withdraw->num_coins),
     147             :             TALER_JSON_pack_array_of_data (
     148             :               "denom_pub_hashes",
     149             :               withdraw->num_coins,
     150             :               withdraw->denom_pub_hashes,
     151             :               sizeof(withdraw->denom_pub_hashes[0])),
     152             :             TALER_JSON_pack_amount (
     153             :               "withdraw_fee",
     154             :               &withdraw_fee),
     155             :             TALER_JSON_pack_amount (
     156             :               "amount",
     157             :               &withdraw->amount_with_fee)
     158             :             );
     159             : 
     160           7 :           if (! withdraw->no_blinding_seed)
     161             :           {
     162           3 :             if (0!=
     163           3 :                 json_object_update_new (
     164             :                   j_entry,
     165           3 :                   GNUNET_JSON_PACK (
     166             :                     GNUNET_JSON_pack_data_auto (
     167             :                       "blinding_seed",
     168             :                       &withdraw->blinding_seed))))
     169             :             {
     170           0 :               GNUNET_break (0);
     171           0 :               json_decref (json_history);
     172           0 :               return NULL;
     173             :             }
     174           3 :             if (0!=
     175           3 :                 json_object_update_new (
     176             :                   j_entry,
     177           3 :                   GNUNET_JSON_PACK (
     178             :                     TALER_JSON_pack_array_of_data (
     179             :                       "cs_r_values",
     180             :                       withdraw->num_cs_r_values,
     181             :                       withdraw->cs_r_values,
     182             :                       sizeof(withdraw->cs_r_values[0])))))
     183             :             {
     184           0 :               GNUNET_break (0);
     185           0 :               json_decref (json_history);
     186           0 :               return NULL;
     187             :             }
     188             :           }
     189           7 :           if (withdraw->age_proof_required)
     190             :           {
     191           0 :             if (0 !=
     192           0 :                 json_object_update_new (
     193             :                   j_entry,
     194           0 :                   GNUNET_JSON_PACK (
     195             :                     GNUNET_JSON_pack_uint64 (
     196             :                       "noreveal_index",
     197             :                       withdraw->noreveal_index),
     198             :                     GNUNET_JSON_pack_data_auto (
     199             :                       "selected_h",
     200             :                       &withdraw->selected_h),
     201             :                     GNUNET_JSON_pack_uint64 (
     202             :                       "max_age",
     203             :                       withdraw->max_age))))
     204             :             {
     205           0 :               GNUNET_break (0);
     206           0 :               json_decref (json_history);
     207           0 :               return NULL;
     208             :             }
     209             :           }
     210             : 
     211           7 :           if (0 !=
     212           7 :               json_array_append_new (
     213             :                 json_history,
     214             :                 j_entry))
     215             :           {
     216           0 :             GNUNET_break (0);
     217           0 :             json_decref (json_history);
     218           0 :             return NULL;
     219             :           }
     220             :         }
     221             :       }
     222             : 
     223           7 :       break;
     224           0 :     case TALER_EXCHANGEDB_RO_RECOUP_COIN:
     225             :       {
     226           0 :         const struct TALER_EXCHANGEDB_Recoup *recoup
     227             :           = pos->details.recoup;
     228             :         struct TALER_ExchangePublicKeyP pub;
     229             :         struct TALER_ExchangeSignatureP sig;
     230             : 
     231           0 :         if (TALER_EC_NONE !=
     232           0 :             TALER_exchange_online_confirm_recoup_sign (
     233             :               &TEH_keys_exchange_sign_,
     234             :               recoup->timestamp,
     235             :               &recoup->value,
     236             :               &recoup->coin.coin_pub,
     237             :               &recoup->reserve_pub,
     238             :               &pub,
     239             :               &sig))
     240             :         {
     241           0 :           GNUNET_break (0);
     242           0 :           json_decref (json_history);
     243           0 :           return NULL;
     244             :         }
     245             : 
     246           0 :         if (0 !=
     247           0 :             json_array_append_new (
     248             :               json_history,
     249           0 :               GNUNET_JSON_PACK (
     250             :                 GNUNET_JSON_pack_string ("type",
     251             :                                          "RECOUP"),
     252             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     253             :                                             &pub),
     254             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     255             :                                             &sig),
     256             :                 GNUNET_JSON_pack_timestamp ("timestamp",
     257             :                                             recoup->timestamp),
     258             :                 TALER_JSON_pack_amount ("amount",
     259             :                                         &recoup->value),
     260             :                 GNUNET_JSON_pack_data_auto ("coin_pub",
     261             :                                             &recoup->coin.coin_pub))))
     262             :         {
     263           0 :           GNUNET_break (0);
     264           0 :           json_decref (json_history);
     265           0 :           return NULL;
     266             :         }
     267             :       }
     268           0 :       break;
     269           0 :     case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
     270             :       {
     271           0 :         const struct TALER_EXCHANGEDB_ClosingTransfer *closing =
     272             :           pos->details.closing;
     273             :         struct TALER_ExchangePublicKeyP pub;
     274             :         struct TALER_ExchangeSignatureP sig;
     275             : 
     276           0 :         if (TALER_EC_NONE !=
     277           0 :             TALER_exchange_online_reserve_closed_sign (
     278             :               &TEH_keys_exchange_sign_,
     279             :               closing->execution_date,
     280             :               &closing->amount,
     281             :               &closing->closing_fee,
     282             :               closing->receiver_account_details,
     283             :               &closing->wtid,
     284           0 :               &pos->details.closing->reserve_pub,
     285             :               &pub,
     286             :               &sig))
     287             :         {
     288           0 :           GNUNET_break (0);
     289           0 :           json_decref (json_history);
     290           0 :           return NULL;
     291             :         }
     292           0 :         if (0 !=
     293           0 :             json_array_append_new (
     294             :               json_history,
     295           0 :               GNUNET_JSON_PACK (
     296             :                 GNUNET_JSON_pack_string ("type",
     297             :                                          "CLOSING"),
     298             :                 TALER_JSON_pack_full_payto ("receiver_account_details",
     299             :                                             closing->receiver_account_details),
     300             :                 GNUNET_JSON_pack_data_auto ("wtid",
     301             :                                             &closing->wtid),
     302             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     303             :                                             &pub),
     304             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     305             :                                             &sig),
     306             :                 GNUNET_JSON_pack_timestamp ("timestamp",
     307             :                                             closing->execution_date),
     308             :                 TALER_JSON_pack_amount ("amount",
     309             :                                         &closing->amount),
     310             :                 TALER_JSON_pack_amount ("closing_fee",
     311             :                                         &closing->closing_fee))))
     312             :         {
     313           0 :           GNUNET_break (0);
     314           0 :           json_decref (json_history);
     315           0 :           return NULL;
     316             :         }
     317             :       }
     318           0 :       break;
     319           8 :     case TALER_EXCHANGEDB_RO_PURSE_MERGE:
     320             :       {
     321           8 :         const struct TALER_EXCHANGEDB_PurseMerge *merge =
     322             :           pos->details.merge;
     323             : 
     324           8 :         if (0 !=
     325           8 :             json_array_append_new (
     326             :               json_history,
     327           8 :               GNUNET_JSON_PACK (
     328             :                 GNUNET_JSON_pack_string ("type",
     329             :                                          "MERGE"),
     330             :                 GNUNET_JSON_pack_data_auto ("h_contract_terms",
     331             :                                             &merge->h_contract_terms),
     332             :                 GNUNET_JSON_pack_data_auto ("merge_pub",
     333             :                                             &merge->merge_pub),
     334             :                 GNUNET_JSON_pack_uint64 ("min_age",
     335             :                                          merge->min_age),
     336             :                 GNUNET_JSON_pack_uint64 ("flags",
     337             :                                          merge->flags),
     338             :                 GNUNET_JSON_pack_data_auto ("purse_pub",
     339             :                                             &merge->purse_pub),
     340             :                 GNUNET_JSON_pack_data_auto ("reserve_sig",
     341             :                                             &merge->reserve_sig),
     342             :                 GNUNET_JSON_pack_timestamp ("merge_timestamp",
     343             :                                             merge->merge_timestamp),
     344             :                 GNUNET_JSON_pack_timestamp ("purse_expiration",
     345             :                                             merge->purse_expiration),
     346             :                 TALER_JSON_pack_amount ("purse_fee",
     347             :                                         &merge->purse_fee),
     348             :                 TALER_JSON_pack_amount ("amount",
     349             :                                         &merge->amount_with_fee),
     350             :                 GNUNET_JSON_pack_bool ("merged",
     351             :                                        merge->merged))))
     352             :         {
     353           0 :           GNUNET_break (0);
     354           0 :           json_decref (json_history);
     355           0 :           return NULL;
     356             :         }
     357             :       }
     358           8 :       break;
     359           0 :     case TALER_EXCHANGEDB_RO_HISTORY_REQUEST:
     360             :       {
     361           0 :         const struct TALER_EXCHANGEDB_HistoryRequest *history =
     362             :           pos->details.history;
     363             : 
     364           0 :         if (0 !=
     365           0 :             json_array_append_new (
     366             :               json_history,
     367           0 :               GNUNET_JSON_PACK (
     368             :                 GNUNET_JSON_pack_string ("type",
     369             :                                          "HISTORY"),
     370             :                 GNUNET_JSON_pack_data_auto ("reserve_sig",
     371             :                                             &history->reserve_sig),
     372             :                 GNUNET_JSON_pack_timestamp ("request_timestamp",
     373             :                                             history->request_timestamp),
     374             :                 TALER_JSON_pack_amount ("amount",
     375             :                                         &history->history_fee))))
     376             :         {
     377           0 :           GNUNET_break (0);
     378           0 :           json_decref (json_history);
     379           0 :           return NULL;
     380             :         }
     381             :       }
     382           0 :       break;
     383             : 
     384           0 :     case TALER_EXCHANGEDB_RO_OPEN_REQUEST:
     385             :       {
     386           0 :         const struct TALER_EXCHANGEDB_OpenRequest *orq =
     387             :           pos->details.open_request;
     388             : 
     389           0 :         if (0 !=
     390           0 :             json_array_append_new (
     391             :               json_history,
     392           0 :               GNUNET_JSON_PACK (
     393             :                 GNUNET_JSON_pack_string ("type",
     394             :                                          "OPEN"),
     395             :                 GNUNET_JSON_pack_uint64 ("requested_min_purses",
     396             :                                          orq->purse_limit),
     397             :                 GNUNET_JSON_pack_data_auto ("reserve_sig",
     398             :                                             &orq->reserve_sig),
     399             :                 GNUNET_JSON_pack_timestamp ("request_timestamp",
     400             :                                             orq->request_timestamp),
     401             :                 GNUNET_JSON_pack_timestamp ("requested_expiration",
     402             :                                             orq->reserve_expiration),
     403             :                 TALER_JSON_pack_amount ("open_fee",
     404             :                                         &orq->open_fee))))
     405             :         {
     406           0 :           GNUNET_break (0);
     407           0 :           json_decref (json_history);
     408           0 :           return NULL;
     409             :         }
     410             :       }
     411           0 :       break;
     412             : 
     413           0 :     case TALER_EXCHANGEDB_RO_CLOSE_REQUEST:
     414             :       {
     415           0 :         const struct TALER_EXCHANGEDB_CloseRequest *crq =
     416             :           pos->details.close_request;
     417             : 
     418           0 :         if (0 !=
     419           0 :             json_array_append_new (
     420             :               json_history,
     421           0 :               GNUNET_JSON_PACK (
     422             :                 GNUNET_JSON_pack_string ("type",
     423             :                                          "CLOSE"),
     424             :                 GNUNET_JSON_pack_data_auto ("reserve_sig",
     425             :                                             &crq->reserve_sig),
     426             :                 GNUNET_is_zero (&crq->target_account_h_payto)
     427             :                 ? GNUNET_JSON_pack_allow_null (
     428             :                   GNUNET_JSON_pack_string ("h_payto",
     429             :                                            NULL))
     430             :                 : GNUNET_JSON_pack_data_auto ("h_payto",
     431             :                                               &crq->target_account_h_payto),
     432             :                 GNUNET_JSON_pack_timestamp ("request_timestamp",
     433             :                                             crq->request_timestamp))))
     434             :         {
     435           0 :           GNUNET_break (0);
     436           0 :           json_decref (json_history);
     437           0 :           return NULL;
     438             :         }
     439             :       }
     440           0 :       break;
     441             :     }
     442             :   }
     443           8 :   return json_history;
     444             : }
     445             : 
     446             : 
     447             : /**
     448             :  * Add the headers we want to set for every /keys response.
     449             :  *
     450             :  * @param cls the key state to use
     451             :  * @param[in,out] response the response to modify
     452             :  */
     453             : static void
     454           8 : add_response_headers (void *cls,
     455             :                       struct MHD_Response *response)
     456             : {
     457             :   (void) cls;
     458           8 :   GNUNET_break (MHD_YES ==
     459             :                 MHD_add_response_header (response,
     460             :                                          MHD_HTTP_HEADER_CACHE_CONTROL,
     461             :                                          "no-cache"));
     462           8 : }
     463             : 
     464             : 
     465             : MHD_RESULT
     466           8 : TEH_handler_reserves_history (
     467             :   struct TEH_RequestContext *rc,
     468             :   const struct TALER_ReservePublicKeyP *reserve_pub)
     469             : {
     470           8 :   struct TALER_EXCHANGEDB_ReserveHistory *rh = NULL;
     471           8 :   uint64_t start_off = 0;
     472             :   struct TALER_Amount balance;
     473             :   uint64_t etag_in;
     474             :   uint64_t etag_out;
     475             :   char etagp[24];
     476             :   struct MHD_Response *resp;
     477             :   unsigned int http_status;
     478             : 
     479           8 :   TALER_MHD_parse_request_number (rc->connection,
     480             :                                   "start",
     481             :                                   &start_off);
     482             :   {
     483             :     struct TALER_ReserveSignatureP reserve_sig;
     484           8 :     bool required = true;
     485             : 
     486           8 :     TALER_MHD_parse_request_header_auto (rc->connection,
     487             :                                          TALER_RESERVE_HISTORY_SIGNATURE_HEADER,
     488             :                                          &reserve_sig,
     489             :                                          required);
     490             : 
     491           8 :     if (GNUNET_OK !=
     492           8 :         TALER_wallet_reserve_history_verify (start_off,
     493             :                                              reserve_pub,
     494             :                                              &reserve_sig))
     495             :     {
     496           0 :       GNUNET_break_op (0);
     497           0 :       return TALER_MHD_reply_with_error (rc->connection,
     498             :                                          MHD_HTTP_FORBIDDEN,
     499             :                                          TALER_EC_EXCHANGE_RESERVE_HISTORY_BAD_SIGNATURE,
     500             :                                          NULL);
     501             :     }
     502             :   }
     503             : 
     504             :   /* Get etag */
     505             :   {
     506             :     const char *etags;
     507             : 
     508           8 :     etags = MHD_lookup_connection_value (rc->connection,
     509             :                                          MHD_HEADER_KIND,
     510             :                                          MHD_HTTP_HEADER_IF_NONE_MATCH);
     511           8 :     if (NULL != etags)
     512             :     {
     513             :       char dummy;
     514             :       unsigned long long ev;
     515             : 
     516           0 :       if (1 != sscanf (etags,
     517             :                        "\"%llu\"%c",
     518             :                        &ev,
     519             :                        &dummy))
     520             :       {
     521           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     522             :                     "Client send malformed `If-None-Match' header `%s'\n",
     523             :                     etags);
     524           0 :         etag_in = 0;
     525             :       }
     526             :       else
     527             :       {
     528           0 :         etag_in = (uint64_t) ev;
     529             :       }
     530             :     }
     531             :     else
     532             :     {
     533           8 :       etag_in = start_off;
     534             :     }
     535             :   }
     536             : 
     537             :   {
     538             :     enum GNUNET_DB_QueryStatus qs;
     539             : 
     540           8 :     qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
     541             :                                           reserve_pub,
     542             :                                           start_off,
     543             :                                           etag_in,
     544             :                                           &etag_out,
     545             :                                           &balance,
     546             :                                           &rh);
     547           8 :     switch (qs)
     548             :     {
     549           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     550           0 :       GNUNET_break (0);
     551           0 :       return TALER_MHD_reply_with_error (rc->connection,
     552             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     553             :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     554             :                                          "get_reserve_history");
     555           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     556           0 :       return TALER_MHD_reply_with_error (rc->connection,
     557             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     558             :                                          TALER_EC_GENERIC_DB_SOFT_FAILURE,
     559             :                                          "get_reserve_history");
     560           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     561           0 :       return TALER_MHD_reply_with_error (rc->connection,
     562             :                                          MHD_HTTP_NOT_FOUND,
     563             :                                          TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
     564             :                                          NULL);
     565           8 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     566             :       /* Handled below */
     567           8 :       break;
     568             :     }
     569             :   }
     570             : 
     571           8 :   GNUNET_snprintf (etagp,
     572             :                    sizeof (etagp),
     573             :                    "\"%llu\"",
     574             :                    (unsigned long long) etag_out);
     575           8 :   if (etag_in == etag_out)
     576             :   {
     577           0 :     TEH_plugin->free_reserve_history (TEH_plugin->cls,
     578             :                                       rh);
     579           0 :     return TEH_RESPONSE_reply_not_modified (rc->connection,
     580             :                                             etagp,
     581             :                                             &add_response_headers,
     582             :                                             NULL);
     583             :   }
     584           8 :   if (NULL == rh)
     585             :   {
     586             :     /* 204: empty history */
     587           0 :     resp = MHD_create_response_from_buffer_static (0,
     588             :                                                    "");
     589           0 :     http_status = MHD_HTTP_NO_CONTENT;
     590             :   }
     591             :   else
     592             :   {
     593             :     json_t *history;
     594             : 
     595           8 :     http_status = MHD_HTTP_OK;
     596           8 :     history = compile_reserve_history (rh);
     597           8 :     TEH_plugin->free_reserve_history (TEH_plugin->cls,
     598             :                                       rh);
     599           8 :     rh = NULL;
     600           8 :     if (NULL == history)
     601             :     {
     602           0 :       GNUNET_break (0);
     603           0 :       return TALER_MHD_reply_with_error (rc->connection,
     604             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     605             :                                          TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
     606             :                                          NULL);
     607             :     }
     608           8 :     resp = TALER_MHD_MAKE_JSON_PACK (
     609             :       TALER_JSON_pack_amount ("balance",
     610             :                               &balance),
     611             :       GNUNET_JSON_pack_array_steal ("history",
     612             :                                     history));
     613             :   }
     614           8 :   add_response_headers (NULL,
     615             :                         resp);
     616           8 :   GNUNET_break (MHD_YES ==
     617             :                 MHD_add_response_header (resp,
     618             :                                          MHD_HTTP_HEADER_ETAG,
     619             :                                          etagp));
     620             :   {
     621             :     MHD_RESULT ret;
     622             : 
     623           8 :     ret = MHD_queue_response (rc->connection,
     624             :                               http_status,
     625             :                               resp);
     626           8 :     GNUNET_break (MHD_YES == ret);
     627           8 :     MHD_destroy_response (resp);
     628           8 :     return ret;
     629             :   }
     630             : }
     631             : 
     632             : 
     633             : /* end of taler-exchange-httpd_reserves_history.c */

Generated by: LCOV version 1.16