LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_responses.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 132 273 48.4 %
Date: 2017-11-25 11:31:41 Functions: 10 20 50.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2017 Inria & GNUnet e.V.
       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_responses.c
      18             :  * @brief API for generating genric replies of the exchange; these
      19             :  *        functions are called TEH_RESPONSE_reply_ and they generate
      20             :  *        and queue MHD response objects for a given connection.
      21             :  * @author Florian Dold
      22             :  * @author Benedikt Mueller
      23             :  * @author Christian Grothoff
      24             :  */
      25             : #include "platform.h"
      26             : #include <zlib.h>
      27             : #include "taler-exchange-httpd_responses.h"
      28             : #include "taler_util.h"
      29             : #include "taler_json_lib.h"
      30             : #include "taler-exchange-httpd_keystate.h"
      31             : 
      32             : 
      33             : /**
      34             :  * Add headers we want to return in every response.
      35             :  * Useful for testing, like if we want to always close
      36             :  * connections.
      37             :  *
      38             :  * @param response response to modify
      39             :  */
      40             : void
      41          80 : TEH_RESPONSE_add_global_headers (struct MHD_Response *response)
      42             : {
      43          80 :   if (TEH_exchange_connection_close)
      44           0 :     GNUNET_break (MHD_YES ==
      45             :                   MHD_add_response_header (response,
      46             :                                            MHD_HTTP_HEADER_CONNECTION,
      47             :                                            "close"));
      48          80 : }
      49             : 
      50             : 
      51             : /**
      52             :  * Is HTTP body deflate compression supported by the client?
      53             :  *
      54             :  * @param connection connection to check
      55             :  * @return #MHD_YES if 'deflate' compression is allowed
      56             :  *
      57             :  * Note that right now we're ignoring q-values, which is technically
      58             :  * not correct, and also do not support "*" anywhere but in a line by
      59             :  * itself.  This should eventually be fixed, see also
      60             :  * https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
      61             :  */
      62             : int
      63          51 : TEH_RESPONSE_can_compress (struct MHD_Connection *connection)
      64             : {
      65             :   const char *ae;
      66             :   const char *de;
      67             : 
      68          51 :   ae = MHD_lookup_connection_value (connection,
      69             :                                     MHD_HEADER_KIND,
      70             :                                     MHD_HTTP_HEADER_ACCEPT_ENCODING);
      71          51 :   if (NULL == ae)
      72          49 :     return MHD_NO;
      73           2 :   de = strstr (ae,
      74             :                "deflate");
      75           2 :   if (NULL == de)
      76           2 :     return MHD_NO;
      77           0 :   if (0 == strcmp (de,
      78             :                    "*"))
      79           0 :     return MHD_YES;
      80           0 :   if ( ( (de == ae) ||
      81           0 :          ( de[-1] == ',') ||
      82           0 :          (de[-1] == ' ') ) &&
      83           0 :        ( (de[strlen ("deflate")] == '\0') ||
      84           0 :          (de[strlen ("deflate")] == ',') ||
      85           0 :          (de[strlen ("deflate")] == ';') ) )
      86           0 :     return MHD_YES;
      87           0 :   return MHD_NO;
      88             : }
      89             : 
      90             : 
      91             : /**
      92             :  * Try to compress a response body.  Updates @a buf and @a buf_size.
      93             :  *
      94             :  * @param[in,out] buf pointer to body to compress
      95             :  * @param[in,out] buf_size pointer to initial size of @a buf
      96             :  * @return #MHD_YES if @a buf was compressed
      97             :  */
      98             : int
      99          17 : TEH_RESPONSE_body_compress (void **buf,
     100             :                             size_t *buf_size)
     101             : {
     102             :   Bytef *cbuf;
     103             :   uLongf cbuf_size;
     104             :   int ret;
     105             : 
     106          17 :   cbuf_size = compressBound (*buf_size);
     107          17 :   cbuf = malloc (cbuf_size);
     108          17 :   if (NULL == cbuf)
     109           0 :     return MHD_NO;
     110          17 :   ret = compress (cbuf,
     111             :                   &cbuf_size,
     112             :                   (const Bytef *) *buf,
     113             :                   *buf_size);
     114          34 :   if ( (Z_OK != ret) ||
     115          17 :        (cbuf_size >= *buf_size) )
     116             :   {
     117             :     /* compression failed */
     118           0 :     free (cbuf);
     119           0 :     return MHD_NO;
     120             :   }
     121          17 :   free (*buf);
     122          17 :   *buf = (void *) cbuf;
     123          17 :   *buf_size = (size_t) cbuf_size;
     124          17 :   return MHD_YES;
     125             : }
     126             : 
     127             : 
     128             : /**
     129             :  * Send JSON object as response.
     130             :  *
     131             :  * @param connection the MHD connection
     132             :  * @param json the json object
     133             :  * @param response_code the http response code
     134             :  * @return MHD result code
     135             :  */
     136             : int
     137          46 : TEH_RESPONSE_reply_json (struct MHD_Connection *connection,
     138             :                          const json_t *json,
     139             :                          unsigned int response_code)
     140             : {
     141             :   struct MHD_Response *resp;
     142             :   void *json_str;
     143             :   size_t json_len;
     144             :   int ret;
     145             :   int comp;
     146             : 
     147          46 :   json_str = json_dumps (json,
     148             :                          JSON_INDENT(2));
     149          46 :   if (NULL == json_str)
     150             :   {
     151           0 :     GNUNET_break (0);
     152           0 :     return MHD_NO;
     153             :   }
     154          46 :   json_len = strlen (json_str);
     155             :   /* try to compress the body */
     156          46 :   comp = MHD_NO;
     157          46 :   if (MHD_YES ==
     158          46 :       TEH_RESPONSE_can_compress (connection))
     159           0 :     comp = TEH_RESPONSE_body_compress (&json_str,
     160             :                                        &json_len);
     161          46 :   resp = MHD_create_response_from_buffer (json_len,
     162             :                                           json_str,
     163             :                                           MHD_RESPMEM_MUST_FREE);
     164          46 :   if (NULL == resp)
     165             :   {
     166           0 :     free (json_str);
     167           0 :     GNUNET_break (0);
     168           0 :     return MHD_NO;
     169             :   }
     170          46 :   TEH_RESPONSE_add_global_headers (resp);
     171          46 :   (void) MHD_add_response_header (resp,
     172             :                                   MHD_HTTP_HEADER_CONTENT_TYPE,
     173             :                                   "application/json");
     174          46 :   if (MHD_YES == comp)
     175             :   {
     176             :     /* Need to indicate to client that body is compressed */
     177           0 :     if (MHD_NO ==
     178           0 :         MHD_add_response_header (resp,
     179             :                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
     180             :                                  "deflate"))
     181             :     {
     182           0 :       GNUNET_break (0);
     183           0 :       MHD_destroy_response (resp);
     184           0 :       return MHD_NO;
     185             :     }
     186             :   }
     187          46 :   ret = MHD_queue_response (connection,
     188             :                             response_code,
     189             :                             resp);
     190          46 :   MHD_destroy_response (resp);
     191          46 :   return ret;
     192             : }
     193             : 
     194             : 
     195             : /**
     196             :  * Function to call to handle the request by building a JSON
     197             :  * reply from a format string and varargs.
     198             :  *
     199             :  * @param connection the MHD connection to handle
     200             :  * @param response_code HTTP response code to use
     201             :  * @param fmt format string for pack
     202             :  * @param ... varargs
     203             :  * @return MHD result code
     204             :  */
     205             : int
     206          41 : TEH_RESPONSE_reply_json_pack (struct MHD_Connection *connection,
     207             :                               unsigned int response_code,
     208             :                               const char *fmt,
     209             :                               ...)
     210             : {
     211             :   json_t *json;
     212             :   va_list argp;
     213             :   int ret;
     214             :   json_error_t jerror;
     215             : 
     216          41 :   va_start (argp, fmt);
     217          41 :   json = json_vpack_ex (&jerror, 0, fmt, argp);
     218          41 :   va_end (argp);
     219          41 :   if (NULL == json)
     220             :   {
     221           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     222             :                 "Failed to pack JSON with format `%s': %s\n",
     223             :                 fmt,
     224             :                 jerror.text);
     225           0 :     GNUNET_break (0);
     226           0 :     return MHD_NO;
     227             :   }
     228          41 :   ret = TEH_RESPONSE_reply_json (connection,
     229             :                                  json,
     230             :                                  response_code);
     231          41 :   json_decref (json);
     232          41 :   return ret;
     233             : }
     234             : 
     235             : 
     236             : /**
     237             :  * Send a response indicating an invalid argument.
     238             :  *
     239             :  * @param connection the MHD connection to use
     240             :  * @param ec error code uniquely identifying the error
     241             :  * @param param_name the parameter that is invalid
     242             :  * @return a MHD result code
     243             :  */
     244             : int
     245           0 : TEH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection,
     246             :                                 enum TALER_ErrorCode ec,
     247             :                                 const char *param_name)
     248             : {
     249           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     250             :                                        MHD_HTTP_BAD_REQUEST,
     251             :                                        "{s:s, s:I, s:s}",
     252             :                                        "error", "invalid parameter",
     253             :                                        "code", (json_int_t) ec,
     254             :                                        "parameter", param_name);
     255             : }
     256             : 
     257             : 
     258             : /**
     259             :  * Send a response indicating an argument refering to a
     260             :  * resource unknown to the exchange (i.e. unknown reserve or
     261             :  * denomination key).
     262             :  *
     263             :  * @param connection the MHD connection to use
     264             :  * @param ec error code uniquely identifying the error
     265             :  * @param param_name the parameter that is invalid
     266             :  * @return a MHD result code
     267             :  */
     268             : int
     269           4 : TEH_RESPONSE_reply_arg_unknown (struct MHD_Connection *connection,
     270             :                                 enum TALER_ErrorCode ec,
     271             :                                 const char *param_name)
     272             : {
     273           4 :   return TEH_RESPONSE_reply_json_pack (connection,
     274             :                                        MHD_HTTP_NOT_FOUND,
     275             :                                        "{s:s, s:I, s:s}",
     276             :                                        "error", "unknown entity referenced",
     277             :                                        "code", (json_int_t) ec,
     278             :                                        "parameter", param_name);
     279             : }
     280             : 
     281             : 
     282             : /**
     283             :  * Send a response indicating an invalid signature.
     284             :  *
     285             :  * @param connection the MHD connection to use
     286             :  * @param ec error code uniquely identifying the error
     287             :  * @param param_name the parameter that is invalid
     288             :  * @return a MHD result code
     289             :  */
     290             : int
     291           0 : TEH_RESPONSE_reply_signature_invalid (struct MHD_Connection *connection,
     292             :                                       enum TALER_ErrorCode ec,
     293             :                                       const char *param_name)
     294             : {
     295           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     296             :                                        MHD_HTTP_UNAUTHORIZED,
     297             :                                        "{s:s, s:I, s:s}",
     298             :                                        "error", "invalid signature",
     299             :                                        "code", (json_int_t) ec,
     300             :                                        "parameter", param_name);
     301             : }
     302             : 
     303             : 
     304             : /**
     305             :  * Send a response indicating a missing argument.
     306             :  *
     307             :  * @param connection the MHD connection to use
     308             :  * @param ec error code uniquely identifying the error
     309             :  * @param param_name the parameter that is missing
     310             :  * @return a MHD result code
     311             :  */
     312             : int
     313           0 : TEH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection,
     314             :                                 enum TALER_ErrorCode ec,
     315             :                                 const char *param_name)
     316             : {
     317           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     318             :                                        MHD_HTTP_BAD_REQUEST,
     319             :                                        "{s:s, s:I, s:s}",
     320             :                                        "error", "missing parameter",
     321             :                                        "code", (json_int_t) ec,
     322             :                                        "parameter", param_name);
     323             : }
     324             : 
     325             : 
     326             : /**
     327             :  * Send a response indicating permission denied.
     328             :  *
     329             :  * @param connection the MHD connection to use
     330             :  * @param ec error code uniquely identifying the error
     331             :  * @param hint hint about why access was denied
     332             :  * @return a MHD result code
     333             :  */
     334             : int
     335           0 : TEH_RESPONSE_reply_permission_denied (struct MHD_Connection *connection,
     336             :                                       enum TALER_ErrorCode ec,
     337             :                                       const char *hint)
     338             : {
     339           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     340             :                                        MHD_HTTP_FORBIDDEN,
     341             :                                        "{s:s, s:I, s:s}",
     342             :                                        "error", "permission denied",
     343             :                                        "code", (json_int_t) ec,
     344             :                                        "hint", hint);
     345             : }
     346             : 
     347             : 
     348             : /**
     349             :  * Send a response indicating an internal error.
     350             :  *
     351             :  * @param connection the MHD connection to use
     352             :  * @param ec error code uniquely identifying the error
     353             :  * @param hint hint about the internal error's nature
     354             :  * @return a MHD result code
     355             :  */
     356             : int
     357           0 : TEH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
     358             :                                    enum TALER_ErrorCode ec,
     359             :                                    const char *hint)
     360             : {
     361           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     362             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     363             :                                        "{s:s, s:I, s:s}",
     364             :                                        "error", "internal error",
     365             :                                        "code", (json_int_t) ec,
     366             :                                        "hint", hint);
     367             : }
     368             : 
     369             : 
     370             : /**
     371             :  * Send a response indicating an external error.
     372             :  *
     373             :  * @param connection the MHD connection to use
     374             :  * @param ec error code uniquely identifying the error
     375             :  * @param hint hint about the error's nature
     376             :  * @return a MHD result code
     377             :  */
     378             : int
     379           0 : TEH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
     380             :                                    enum TALER_ErrorCode ec,
     381             :                                    const char *hint)
     382             : {
     383           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     384             :                                        MHD_HTTP_BAD_REQUEST,
     385             :                                        "{s:s, s:I, s:s}",
     386             :                                        "error", "client error",
     387             :                                        "code", (json_int_t) ec,
     388             :                                        "hint", hint);
     389             : }
     390             : 
     391             : 
     392             : /**
     393             :  * Send a response indicating an error committing a
     394             :  * transaction (concurrent interference).
     395             :  *
     396             :  * @param connection the MHD connection to use
     397             :  * @param ec error code uniquely identifying the error
     398             :  * @return a MHD result code
     399             :  */
     400             : int
     401           0 : TEH_RESPONSE_reply_commit_error (struct MHD_Connection *connection,
     402             :                                  enum TALER_ErrorCode ec)
     403             : {
     404           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     405             :                                        MHD_HTTP_BAD_REQUEST,
     406             :                                        "{s:s, s:I}",
     407             :                                        "error", "commit failure",
     408             :                                        "code", (json_int_t) ec);
     409             : }
     410             : 
     411             : 
     412             : /**
     413             :  * Send a response indicating a failure to talk to the Exchange's
     414             :  * database.
     415             :  *
     416             :  * @param connection the MHD connection to use
     417             :  * @param ec error code uniquely identifying the error
     418             :  * @return a MHD result code
     419             :  */
     420             : int
     421           0 : TEH_RESPONSE_reply_internal_db_error (struct MHD_Connection *connection,
     422             :                                       enum TALER_ErrorCode ec)
     423             : {
     424           0 :   return TEH_RESPONSE_reply_internal_error (connection,
     425             :                                             ec,
     426             :                                             "Failure in database interaction");
     427             : }
     428             : 
     429             : 
     430             : /**
     431             :  * Send a response indicating that the request was too big.
     432             :  *
     433             :  * @param connection the MHD connection to use
     434             :  * @return a MHD result code
     435             :  */
     436             : int
     437           0 : TEH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection)
     438             : {
     439             :   struct MHD_Response *resp;
     440             :   int ret;
     441             : 
     442           0 :   resp = MHD_create_response_from_buffer (0,
     443             :                                           NULL,
     444             :                                           MHD_RESPMEM_PERSISTENT);
     445           0 :   if (NULL == resp)
     446           0 :     return MHD_NO;
     447           0 :   TEH_RESPONSE_add_global_headers (resp);
     448           0 :   ret = MHD_queue_response (connection,
     449             :                             MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
     450             :                             resp);
     451           0 :   MHD_destroy_response (resp);
     452           0 :   return ret;
     453             : }
     454             : 
     455             : 
     456             : /**
     457             :  * Send a response indicating that the JSON was malformed.
     458             :  *
     459             :  * @param connection the MHD connection to use
     460             :  * @return a MHD result code
     461             :  */
     462             : int
     463           0 : TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection)
     464             : {
     465           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     466             :                                        MHD_HTTP_BAD_REQUEST,
     467             :                                        "{s:s, s:I}",
     468             :                                        "error", "invalid json",
     469             :                                        "code", (json_int_t) TALER_EC_JSON_INVALID);
     470             : }
     471             : 
     472             : 
     473             : /**
     474             :  * Compile the transaction history of a coin into a JSON object.
     475             :  *
     476             :  * @param tl transaction history to JSON-ify
     477             :  * @return json representation of the @a rh, NULL on error
     478             :  */
     479             : json_t *
     480           5 : TEH_RESPONSE_compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
     481             : {
     482             :   json_t *history;
     483             : 
     484           5 :   history = json_array ();
     485           5 :   if (NULL == history)
     486             :   {
     487           0 :     GNUNET_break (0); /* out of memory!? */
     488           0 :     return NULL;
     489             :   }
     490          12 :   for (const struct TALER_EXCHANGEDB_TransactionList *pos = tl; NULL != pos; pos = pos->next)
     491             :   {
     492           7 :     switch (pos->type)
     493             :     {
     494             :     case TALER_EXCHANGEDB_TT_DEPOSIT:
     495             :       {
     496             :         struct TALER_DepositRequestPS dr;
     497           5 :         const struct TALER_EXCHANGEDB_Deposit *deposit = pos->details.deposit;
     498             : 
     499           5 :         dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
     500           5 :         dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
     501           5 :         dr.h_contract_terms = deposit->h_contract_terms;
     502           5 :         dr.h_wire = deposit->h_wire;
     503           5 :         dr.timestamp = GNUNET_TIME_absolute_hton (deposit->timestamp);
     504           5 :         dr.refund_deadline = GNUNET_TIME_absolute_hton (deposit->refund_deadline);
     505           5 :         TALER_amount_hton (&dr.amount_with_fee,
     506             :                            &deposit->amount_with_fee);
     507           5 :         TALER_amount_hton (&dr.deposit_fee,
     508             :                            &deposit->deposit_fee);
     509           5 :         dr.merchant = deposit->merchant_pub;
     510           5 :         dr.coin_pub = deposit->coin.coin_pub;
     511             :         /* internal sanity check before we hand out a bogus sig... */
     512           5 :         if (GNUNET_OK !=
     513           5 :             GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
     514             :                                         &dr.purpose,
     515             :                                         &deposit->csig.eddsa_signature,
     516             :                                         &deposit->coin.coin_pub.eddsa_pub))
     517             :         {
     518           0 :           GNUNET_break (0);
     519           0 :           json_decref (history);
     520           0 :           return NULL;
     521             :         }
     522             : 
     523           5 :         GNUNET_assert (0 ==
     524             :                        json_array_append_new (history,
     525             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
     526             :                                                          "type", "DEPOSIT",
     527             :                                                          "amount", TALER_JSON_from_amount (&deposit->amount_with_fee),
     528             :                                                          "deposit_fee", TALER_JSON_from_amount (&deposit->deposit_fee),
     529             :                                                          "timestamp", GNUNET_JSON_from_time_abs (deposit->timestamp),
     530             :                                                          "refund_deadline", GNUNET_JSON_from_time_abs (deposit->refund_deadline),
     531             :                                                          "merchant_pub", GNUNET_JSON_from_data_auto (&deposit->merchant_pub),
     532             :                                                          "h_contract_terms", GNUNET_JSON_from_data_auto (&deposit->h_contract_terms),
     533             :                                                          "h_wire", GNUNET_JSON_from_data_auto (&deposit->h_wire),
     534             :                                                          "coin_sig", GNUNET_JSON_from_data_auto (&deposit->csig))));
     535           5 :         break;
     536             :       }
     537             :     case TALER_EXCHANGEDB_TT_REFRESH_MELT:
     538             :       {
     539             :         struct TALER_RefreshMeltCoinAffirmationPS ms;
     540           1 :         const struct TALER_EXCHANGEDB_RefreshMelt *melt = pos->details.melt;
     541             : 
     542           1 :         ms.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
     543           1 :         ms.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
     544           1 :         ms.session_hash = melt->session_hash;
     545           1 :         TALER_amount_hton (&ms.amount_with_fee,
     546             :                            &melt->amount_with_fee);
     547           1 :         TALER_amount_hton (&ms.melt_fee,
     548             :                            &melt->melt_fee);
     549           1 :         ms.coin_pub = melt->coin.coin_pub;
     550             :         /* internal sanity check before we hand out a bogus sig... */
     551           1 :         if (GNUNET_OK !=
     552           1 :             GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
     553             :                                         &ms.purpose,
     554             :                                         &melt->coin_sig.eddsa_signature,
     555             :                                         &melt->coin.coin_pub.eddsa_pub))
     556             :         {
     557           0 :           GNUNET_break (0);
     558           0 :           json_decref (history);
     559           0 :           return NULL;
     560             :         }
     561             : 
     562           1 :         GNUNET_assert (0 ==
     563             :                        json_array_append_new (history,
     564             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o}",
     565             :                                                          "type", "MELT",
     566             :                                                          "amount", TALER_JSON_from_amount (&melt->amount_with_fee),
     567             :                                                          "melt_fee", TALER_JSON_from_amount (&melt->melt_fee),
     568             :                                                          "session_hash", GNUNET_JSON_from_data_auto (&melt->session_hash),
     569             :                                                          "coin_sig", GNUNET_JSON_from_data_auto (&melt->coin_sig))));
     570             :       }
     571           1 :       break;
     572             :     case TALER_EXCHANGEDB_TT_REFUND:
     573             :       {
     574             :         struct TALER_RefundRequestPS rr;
     575           0 :         const struct TALER_EXCHANGEDB_Refund *refund = pos->details.refund;
     576             :         struct TALER_Amount value;
     577             : 
     578           0 :         if (GNUNET_OK !=
     579           0 :             TALER_amount_subtract (&value,
     580             :                                    &refund->refund_amount,
     581             :                                    &refund->refund_fee))
     582             :         {
     583           0 :           GNUNET_break (0);
     584           0 :           json_decref (history);
     585           0 :           return NULL;
     586             :         }
     587           0 :         rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
     588           0 :         rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
     589           0 :         rr.h_contract_terms = refund->h_contract_terms;
     590           0 :         rr.coin_pub = refund->coin.coin_pub;
     591           0 :         rr.merchant = refund->merchant_pub;
     592           0 :         rr.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
     593           0 :         TALER_amount_hton (&rr.refund_amount,
     594             :                            &refund->refund_amount);
     595           0 :         TALER_amount_hton (&rr.refund_fee,
     596             :                            &refund->refund_fee);
     597             :         /* internal sanity check before we hand out a bogus sig... */
     598           0 :         if (GNUNET_OK !=
     599           0 :             GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
     600             :                                         &rr.purpose,
     601             :                                         &refund->merchant_sig.eddsa_sig,
     602             :                                         &refund->merchant_pub.eddsa_pub))
     603             :         {
     604           0 :           GNUNET_break (0);
     605           0 :           json_decref (history);
     606           0 :           return NULL;
     607             :         }
     608             : 
     609           0 :         GNUNET_assert (0 ==
     610             :                        json_array_append_new (history,
     611             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o, s:I, s:o}",
     612             :                                                          "type", "REFUND",
     613             :                                                          "amount", TALER_JSON_from_amount (&value),
     614             :                                                          "refund_fee", TALER_JSON_from_amount (&refund->refund_fee),
     615             :                                                          "h_contract_terms", GNUNET_JSON_from_data_auto (&refund->h_contract_terms),
     616             :                                                          "merchant_pub", GNUNET_JSON_from_data_auto (&refund->merchant_pub),
     617             :                                                          "rtransaction_id", (json_int_t) refund->rtransaction_id,
     618             :                                                          "merchant_sig", GNUNET_JSON_from_data_auto (&refund->merchant_sig))));
     619             :       }
     620           0 :       break;
     621             :     case TALER_EXCHANGEDB_TT_PAYBACK:
     622             :       {
     623           1 :         const struct TALER_EXCHANGEDB_Payback *payback = pos->details.payback;
     624             :         struct TALER_PaybackConfirmationPS pc;
     625             :         struct TALER_ExchangePublicKeyP epub;
     626             :         struct TALER_ExchangeSignatureP esig;
     627             : 
     628           1 :         pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
     629           1 :         pc.purpose.size = htonl (sizeof (pc));
     630           1 :         pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp);
     631           1 :         TALER_amount_hton (&pc.payback_amount,
     632             :                            &payback->value);
     633           1 :         pc.coin_pub = payback->coin.coin_pub;
     634           1 :         pc.reserve_pub = payback->reserve_pub;
     635           1 :         if (GNUNET_OK !=
     636           1 :             TEH_KS_sign (&pc.purpose,
     637             :                          &epub,
     638             :                          &esig))
     639             :         {
     640           0 :           GNUNET_break (0);
     641           0 :           json_decref (history);
     642           0 :           return NULL;
     643             :         }
     644           1 :         GNUNET_assert (0 ==
     645             :                        json_array_append_new (history,
     646             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
     647             :                                                          "type", "PAYBACK",
     648             :                                                          "amount", TALER_JSON_from_amount (&payback->value),
     649             :                                                          "exchange_sig", GNUNET_JSON_from_data_auto (&esig),
     650             :                                                          "exchange_pub", GNUNET_JSON_from_data_auto (&epub),
     651             :                                                          "reserve_pub", GNUNET_JSON_from_data_auto (&payback->reserve_pub),
     652             :                                                          "timestamp", GNUNET_JSON_from_time_abs (payback->timestamp))));
     653             :       }
     654           1 :       break;
     655             :     default:
     656           0 :       GNUNET_assert (0);
     657             :     }
     658             :   }
     659           5 :   return history;
     660             : }
     661             : 
     662             : 
     663             : /**
     664             :  * Send proof that a request is invalid to client because of
     665             :  * insufficient funds.  This function will create a message with all
     666             :  * of the operations affecting the coin that demonstrate that the coin
     667             :  * has insufficient value.
     668             :  *
     669             :  * @param connection connection to the client
     670             :  * @param ec error code to return
     671             :  * @param tl transaction list to use to build reply
     672             :  * @return MHD result code
     673             :  */
     674             : int
     675           4 : TEH_RESPONSE_reply_coin_insufficient_funds (struct MHD_Connection *connection,
     676             :                                             enum TALER_ErrorCode ec,
     677             :                                             const struct TALER_EXCHANGEDB_TransactionList *tl)
     678             : {
     679             :   json_t *history;
     680             : 
     681           4 :   history = TEH_RESPONSE_compile_transaction_history (tl);
     682           4 :   if (NULL == history)
     683           0 :     return TEH_RESPONSE_reply_internal_error (connection,
     684             :                                               TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
     685             :                                               "failed to convert transaction history to JSON");
     686           4 :   return TEH_RESPONSE_reply_json_pack (connection,
     687             :                                        MHD_HTTP_FORBIDDEN,
     688             :                                        "{s:s, s:I, s:o}",
     689             :                                        "error", "insufficient funds",
     690             :                                        "code", (json_int_t) ec,
     691             :                                        "history", history);
     692             : }
     693             : 
     694             : 
     695             : /**
     696             :  * Compile the history of a reserve into a JSON object
     697             :  * and calculate the total balance.
     698             :  *
     699             :  * @param rh reserve history to JSON-ify
     700             :  * @param[out] balance set to current reserve balance
     701             :  * @return json representation of the @a rh, NULL on error
     702             :  */
     703             : json_t *
     704           3 : TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
     705             :                                       struct TALER_Amount *balance)
     706             : {
     707             :   struct TALER_Amount deposit_total;
     708             :   struct TALER_Amount withdraw_total;
     709             :   json_t *json_history;
     710             :   int ret;
     711             : 
     712           3 :   json_history = json_array ();
     713           3 :   ret = 0;
     714          10 :   for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh; NULL != pos; pos = pos->next)
     715             :   {
     716           7 :     switch (pos->type)
     717             :     {
     718             :     case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
     719           3 :       if (0 == (1 & ret))
     720           3 :         deposit_total = pos->details.bank->amount;
     721             :       else
     722           0 :         if (GNUNET_OK !=
     723           0 :             TALER_amount_add (&deposit_total,
     724             :                               &deposit_total,
     725           0 :                               &pos->details.bank->amount))
     726             :         {
     727           0 :           GNUNET_break (0);
     728           0 :           json_decref (json_history);
     729           0 :           return NULL;
     730             :         }
     731           3 :       ret |= 1;
     732           3 :       GNUNET_assert (0 ==
     733             :                      json_array_append_new (json_history,
     734             :                                             json_pack ("{s:s, s:O, s:o, s:o}",
     735             :                                                        "type", "DEPOSIT",
     736             :                                                        "sender_account_details", pos->details.bank->sender_account_details,
     737             :                                                        "wire_reference", GNUNET_JSON_from_data (pos->details.bank->wire_reference,
     738             :                                                                                                 pos->details.bank->wire_reference_size),
     739             :                                                        "amount", TALER_JSON_from_amount (&pos->details.bank->amount))));
     740           3 :       break;
     741             :     case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
     742             :       {
     743             :         struct GNUNET_HashCode h_denom_pub;
     744             :         struct TALER_Amount value;
     745             : 
     746           3 :         value = pos->details.withdraw->amount_with_fee;
     747           3 :         if (0 == (2 & ret))
     748             :         {
     749           3 :           withdraw_total = value;
     750             :         }
     751             :         else
     752             :         {
     753           0 :           if (GNUNET_OK !=
     754           0 :               TALER_amount_add (&withdraw_total,
     755             :                                 &withdraw_total,
     756             :                                 &value))
     757             :           {
     758           0 :             GNUNET_break (0);
     759           0 :             json_decref (json_history);
     760           0 :             return NULL;
     761             :           }
     762             :         }
     763           3 :         ret |= 2;
     764           3 :         GNUNET_CRYPTO_rsa_public_key_hash (pos->details.withdraw->denom_pub.rsa_public_key,
     765             :                                            &h_denom_pub);
     766           3 :         GNUNET_assert (0 ==
     767             :                        json_array_append_new (json_history,
     768             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
     770             :                                                          "reserve_sig", GNUNET_JSON_from_data_auto (&pos->details.withdraw->reserve_sig),
     771             :                                                          "h_coin_envelope", GNUNET_JSON_from_data_auto (&pos->details.withdraw->h_coin_envelope),
     772             :                                                          "h_denom_pub", GNUNET_JSON_from_data_auto (&h_denom_pub),
     773             :                                                          "withdraw_fee", TALER_JSON_from_amount (&pos->details.withdraw->withdraw_fee),
     774             :                                                          "amount", TALER_JSON_from_amount (&value))));
     775             :       }
     776           3 :       break;
     777             :     case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
     778             :       {
     779             :         const struct TALER_EXCHANGEDB_Payback *payback;
     780             :         struct TALER_PaybackConfirmationPS pc;
     781             :         struct TALER_ExchangePublicKeyP pub;
     782             :         struct TALER_ExchangeSignatureP sig;
     783             : 
     784           1 :         payback = pos->details.payback;
     785           1 :         if (0 == (1 & ret))
     786           0 :           deposit_total = payback->value;
     787             :         else
     788           1 :           if (GNUNET_OK !=
     789           1 :               TALER_amount_add (&deposit_total,
     790             :                                 &deposit_total,
     791             :                                 &payback->value))
     792             :           {
     793           0 :             GNUNET_break (0);
     794           0 :             json_decref (json_history);
     795           0 :             return NULL;
     796             :           }
     797           1 :         ret |= 1;
     798           1 :         pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
     799           1 :         pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS));
     800           1 :         pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp);
     801           1 :         TALER_amount_hton (&pc.payback_amount,
     802             :                            &payback->value);
     803           1 :         pc.coin_pub = payback->coin.coin_pub;
     804           1 :         pc.reserve_pub = payback->reserve_pub;
     805           1 :         if (GNUNET_OK !=
     806           1 :             TEH_KS_sign (&pc.purpose,
     807             :                          &pub,
     808             :                          &sig))
     809             :         {
     810           0 :           GNUNET_break (0);
     811           0 :           json_decref (json_history);
     812           0 :           return NULL;
     813             :         }
     814             : 
     815           1 :         GNUNET_assert (0 ==
     816             :                        json_array_append_new (json_history,
     817             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
     818             :                                                          "type", "PAYBACK",
     819             :                                                          "exchange_pub", GNUNET_JSON_from_data_auto (&pub),
     820             :                                                          "exchange_sig", GNUNET_JSON_from_data_auto (&sig),
     821             :                                                          "timestamp", GNUNET_JSON_from_time_abs (payback->timestamp),
     822             :                                                          "amount", TALER_JSON_from_amount (&payback->value),
     823             :                                                          "coin_pub", GNUNET_JSON_from_data_auto (&payback->coin.coin_pub))));
     824             :       }
     825           1 :       break;
     826             :     case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
     827             :       {
     828             :         struct TALER_ReserveCloseConfirmationPS rcc;
     829             :         struct TALER_ExchangePublicKeyP pub;
     830             :         struct TALER_ExchangeSignatureP sig;
     831             :         struct TALER_Amount value;
     832             : 
     833           0 :         value = pos->details.closing->amount;
     834           0 :         if (0 == (2 & ret))
     835             :         {
     836           0 :           withdraw_total = value;
     837             :         }
     838             :         else
     839             :         {
     840           0 :           if (GNUNET_OK !=
     841           0 :               TALER_amount_add (&withdraw_total,
     842             :                                 &withdraw_total,
     843             :                                 &value))
     844             :           {
     845           0 :             json_decref (json_history);
     846           0 :             return NULL;
     847             :           }
     848             :         }
     849           0 :         ret |= 2;
     850           0 :         rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
     851           0 :         rcc.purpose.size = htonl (sizeof (struct TALER_ReserveCloseConfirmationPS));
     852           0 :         rcc.timestamp = GNUNET_TIME_absolute_hton (pos->details.closing->execution_date);
     853           0 :         TALER_amount_hton (&rcc.closing_amount,
     854             :                            &value);
     855           0 :         TALER_amount_hton (&rcc.closing_fee,
     856           0 :                            &pos->details.closing->closing_fee);
     857           0 :         rcc.reserve_pub = pos->details.closing->reserve_pub;
     858           0 :         if (GNUNET_OK !=
     859           0 :             TALER_JSON_hash (pos->details.closing->receiver_account_details,
     860             :                              &rcc.h_wire))
     861             :         {
     862           0 :           GNUNET_break (0);
     863           0 :           json_decref (json_history);
     864           0 :           return NULL;
     865             :         }
     866           0 :         rcc.wtid = pos->details.closing->wtid;
     867           0 :         if (GNUNET_OK !=
     868           0 :             TEH_KS_sign (&rcc.purpose,
     869             :                          &pub,
     870             :                          &sig))
     871             :         {
     872           0 :           GNUNET_break (0);
     873           0 :           json_decref (json_history);
     874           0 :           return NULL;
     875             :         }
     876           0 :         GNUNET_assert (0 ==
     877             :                        json_array_append_new (json_history,
     878             :                                               json_pack ("{s:s, s:O, s:o, s:o, s:o, s:o, s:o, s:o}",
     879             :                                                          "type", "CLOSING",
     880             :                                                          "receiver_account_details", pos->details.closing->receiver_account_details,
     881             :                                                          "wtid", GNUNET_JSON_from_data_auto (&pos->details.closing->wtid),
     882             :                                                          "exchange_pub", GNUNET_JSON_from_data_auto (&pub),
     883             :                                                          "exchange_sig", GNUNET_JSON_from_data_auto (&sig),
     884             :                                                          "timestamp", GNUNET_JSON_from_time_abs (pos->details.closing->execution_date),
     885             :                                                          "amount", TALER_JSON_from_amount (&value),
     886             :                                                          "closing_fee", TALER_JSON_from_amount (&pos->details.closing->closing_fee))));
     887             :       }
     888           0 :       break;
     889             :     }
     890             :   }
     891           3 :   if (0 == (1 & ret))
     892             :   {
     893           0 :     GNUNET_break (0);
     894           0 :     json_decref (json_history);
     895           0 :     return NULL;
     896             :   }
     897           3 :   if (0 == (2 & ret))
     898             :   {
     899             :     /* did not encounter any withdraw operations, set to zero */
     900           0 :     GNUNET_assert (GNUNET_OK ==
     901             :                    TALER_amount_get_zero (deposit_total.currency,
     902             :                                           &withdraw_total));
     903             :   }
     904           3 :   if (GNUNET_SYSERR ==
     905           3 :       TALER_amount_subtract (balance,
     906             :                              &deposit_total,
     907             :                              &withdraw_total))
     908             :   {
     909           0 :     GNUNET_break (0);
     910           0 :     json_decref (json_history);
     911           0 :     return NULL;
     912             :   }
     913             : 
     914           3 :   return json_history;
     915             : }
     916             : 
     917             : 
     918             : /**
     919             :  * A merchant asked for details about a deposit, but
     920             :  * we do not know anything about the deposit. Generate the
     921             :  * 404 reply.
     922             :  *
     923             :  * @param connection connection to the client
     924             :  * @param ec Taler error code
     925             :  * @return MHD result code
     926             :  */
     927             : int
     928           1 : TEH_RESPONSE_reply_transaction_unknown (struct MHD_Connection *connection,
     929             :                                         enum TALER_ErrorCode ec)
     930             : {
     931           1 :   return TEH_RESPONSE_reply_json_pack (connection,
     932             :                                        MHD_HTTP_NOT_FOUND,
     933             :                                        "{s:s, s:I}",
     934             :                                        "error", "Deposit unknown",
     935             :                                        "code", (json_int_t) ec);
     936             : }
     937             : 
     938             : 
     939             : /* end of taler-exchange-httpd_responses.c */

Generated by: LCOV version 1.13