LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_responses.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 130 254 51.2 %
Date: 2017-09-17 17:24:28 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,
|spancl`ss="lineNum">     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
     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 :         TEH_KS_sign (&pc.purpose,
     636             :                      &epub,
     637             :                      &esig);
     638           1 :         GNUNET_assert (0 ==
     639             :                        json_array_append_new (history,
     640             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
     641             :                                                          "type", "PAYBACK",
     642             :                                                          "amount", TALER_JSON_from_amount (&payback->value),
     643             :                                                          "exchange_sig", GNUNET_JSON_from_data_auto (&esig),
     644             :                                                          "exchange_pub", GNUNET_JSON_from_data_auto (&epub),
     645             :                                                          "reserve_pub", GNUNET_JSON_from_data_auto (&payback->reserve_pub),
     646             :                                                          "timestamp", GNUNET_JSON_from_time_abs (payback->timestamp))));
     647             :       }
     648           1 :       break;
     649             :     default:
     650           0 :       GNUNET_assert (0);
     651             :     }
     652             :   }
     653           5 :   return history;
     654             : }
     655             : 
     656             : 
     657             : /**
     658             :  * Send proof that a request is invalid to client because of
     659             :  * insufficient funds.  This function will create a message with all
     660             :  * of the operations affecting the coin that demonstrate that the coin
     661             :  * has insufficient value.
     662             :  *
     663             :  * @param connection connection to the client
     664             :  * @param ec error code to return
     665             :  * @param tl transaction list to use to build reply
     666             :  * @return MHD result code
     667             :  */
     668             : int
     669           4 : TEH_RESPONSE_reply_coin_insufficient_funds (struct MHD_Connection *connection,
     670             :                                             enum TALER_ErrorCode ec,
     671             :                                             const struct TALER_EXCHANGEDB_TransactionList *tl)
     672             : {
     673             :   json_t *history;
     674             : 
     675           4 :   history = TEH_RESPONSE_compile_transaction_history (tl);
     676           4 :   if (NULL == history)
     677           0 :     return TEH_RESPONSE_reply_internal_error (connection,
     678             :                                               TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
     679             :                                               "failed to convert transaction history to JSON");
     680           4 :   return TEH_RESPONSE_reply_json_pack (connection,
     681             :                                        MHD_HTTP_FORBIDDEN,
     682             :                                        "{s:s, s:I, s:o}",
     683             :                                        "error", "insufficient funds",
     684             :                                        "code", (json_int_t) ec,
     685             :                                        "history", history);
     686             : }
     687             : 
     688             : 
     689             : /**
     690             :  * Compile the history of a reserve into a JSON object
     691             :  * and calculate the total balance.
     692             :  *
     693             :  * @param rh reserve history to JSON-ify
     694             :  * @param[out] balance set to current reserve balance
     695             :  * @return json representation of the @a rh, NULL on error
     696             :  */
     697             : json_t *
     698           3 : TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
     699             :                                       struct TALER_Amount *balance)
     700             : {
     701             :   struct TALER_Amount deposit_total;
     702             :   struct TALER_Amount withdraw_total;
     703             :   json_t *json_history;
     704             :   int ret;
     705             : 
     706           3 :   json_history = json_array ();
     707           3 :   ret = 0;
     708          10 :   for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh; NULL != pos; pos = pos->next)
     709             :   {
     710           7 :     switch (pos->type)
     711             :     {
     712             :     case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
     713           3 :       if (0 == (1 & ret))
     714           3 :         deposit_total = pos->details.bank->amount;
     715             :       else
     716           0 :         if (GNUNET_OK !=
     717           0 :             TALER_amount_add (&deposit_total,
     718             :                               &deposit_total,
     719           0 :                               &pos->details.bank->amount))
     720             :         {
     721           0 :           json_decref (json_history);
     722           0 :           return NULL;
     723             :         }
     724           3 :       ret |= 1;
     725           3 :       GNUNET_assert (0 ==
     726             :                      json_array_append_new (json_history,
     727             :                                             json_pack ("{s:s, s:O, s:o, s:o}",
     728             :                                                        "type", "DEPOSIT",
     729             :                                                        "sender_account_details", pos->details.bank->sender_account_details,
     730             :                                                        "wire_reference", GNUNET_JSON_from_data (pos->details.bank->wire_reference,
     731             :                                                                                                 pos->details.bank->wire_reference_size),
     732             :                                                        "amount", TALER_JSON_from_amount (&pos->details.bank->amount))));
     733           3 :       break;
     734             :     case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
     735             :       {
     736             :         struct GNUNET_HashCode h_denom_pub;
     737             :         struct TALER_Amount value;
     738             : 
     739           3 :         value = pos->details.withdraw->amount_with_fee;
     740           3 :         if (0 == (2 & ret))
     741             :         {
     742           3 :           withdraw_total = value;
     743             :         }
     744             :         else
     745             :         {
     746           0 :           if (GNUNET_OK !=
     747           0 :               TALER_amount_add (&withdraw_total,
     748             :                                 &withdraw_total,
     749             :                                 &value))
     750             :           {
     751           0 :             json_decref (json_history);
     752           0 :             return NULL;
     753             :           }
     754             :         }
     755           3 :         ret |= 2;
     756           3 :         GNUNET_CRYPTO_rsa_public_key_hash (pos->details.withdraw->denom_pub.rsa_public_key,
     757             :                                            &h_denom_pub);
     758           3 :         GNUNET_assert (0 ==
     759             :                        json_array_append_new (json_history,
     760             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
     761             :                                                          "type", "WITHDRAW",
     762             :                                                          "reserve_sig", GNUNET_JSON_from_data_auto (&pos->details.withdraw->reserve_sig),
     763             :                                                          "h_coin_envelope", GNUNET_JSON_from_data_auto (&pos->details.withdraw->h_coin_envelope),
     764             :                                                          "h_denom_pub", GNUNET_JSON_from_data_auto (&h_denom_pub),
     765             :                                                          "withdraw_fee", TALER_JSON_from_amount (&pos->details.withdraw->withdraw_fee),
     766             :                                                          "amount", TALER_JSON_from_amount (&value))));
     767             :       }
     768           3 :       break;
     769             :     case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
     770             :       {
     771             :         const struct TALER_EXCHANGEDB_Payback *payback;
     772             :         struct TALER_PaybackConfirmationPS pc;
     773             :         struct TALER_ExchangePublicKeyP pub;
     774             :         struct TALER_ExchangeSignatureP sig;
     775             : 
     776           1 :         payback = pos->details.payback;
     777           1 :         if (0 == (1 & ret))
     778           0 :           deposit_total = payback->value;
     779             :         else
     780           1 :           if (GNUNET_OK !=
     781           1 :               TALER_amount_add (&deposit_total,
     782             :                                 &deposit_total,
     783             :                                 &payback->value))
     784             :           {
     785           0 :             json_decref (json_history);
     786           0 :             return NULL;
     787             :           }
     788           1 :         ret |= 1;
     789           1 :         pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
     790           1 :         pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS));
     791           1 :         pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp);
     792           1 :         TALER_amount_hton (&pc.payback_amount,
     793             :                            &payback->value);
     794           1 :         pc.coin_pub = payback->coin.coin_pub;
     795           1 :         pc.reserve_pub = payback->reserve_pub;
     796           1 :         TEH_KS_sign (&pc.purpose,
     797             :                      &pub,
     798             :                      &sig);
     799             : 
     800           1 :         GNUNET_assert (0 ==
     801             :                        json_array_append_new (json_history,
     802             :                                               json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
     803             :                                                          "type", "PAYBACK",
     804             :                                                          "exchange_pub", GNUNET_JSON_from_data_auto (&pub),
     805             :                                                          "exchange_sig", GNUNET_JSON_from_data_auto (&sig),
     806             :                                                          "timestamp", GNUNET_JSON_from_time_abs (payback->timestamp),
     807             :                                                          "amount", TALER_JSON_from_amount (&payback->value),
     808             :                                                          "coin_pub", GNUNET_JSON_from_data_auto (&payback->coin.coin_pub))));
     809             :       }
     810           1 :       break;
     811             :     case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
     812             :       {
     813             :         struct TALER_ReserveCloseConfirmationPS rcc;
     814             :         struct TALER_ExchangePublicKeyP pub;
     815             :         struct TALER_ExchangeSignatureP sig;
     816             :         struct TALER_Amount value;
     817             : 
     818           0 :         value = pos->details.closing->amount;
     819           0 :         if (0 == (2 & ret))
     820             :         {
     821           0 :           withdraw_total = value;
     822             :         }
     823             :         else
     824             :         {
     825           0 :           if (GNUNET_OK !=
     826           0 :               TALER_amount_add (&withdraw_total,
     827             :                                 &withdraw_total,
     828             :                                 &value))
     829             :           {
     830           0 :             json_decref (json_history);
     831           0 :             return NULL;
     832             :           }
     833             :         }
     834           0 :         ret |= 2;
     835           0 :         rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
     836           0 :         rcc.purpose.size = htonl (sizeof (struct TALER_ReserveCloseConfirmationPS));
     837           0 :         rcc.timestamp = GNUNET_TIME_absolute_hton (pos->details.closing->execution_date);
     838           0 :         TALER_amount_hton (&rcc.closing_amount,
     839             :                            &value);
     840           0 :         TALER_amount_hton (&rcc.closing_fee,
     841           0 :                            &pos->details.closing->closing_fee);
     842           0 :         rcc.reserve_pub = pos->details.closing->reserve_pub;
     843           0 :         TALER_JSON_hash (pos->details.closing->receiver_account_details,
     844             :                          &rcc.h_wire);
     845           0 :         rcc.wtid = pos->details.closing->wtid;
     846           0 :         TEH_KS_sign (&rcc.purpose,
     847             :                      &pub,
     848             :                      &sig);
     849           0 :         GNUNET_assert (0 ==
     850             :                        json_array_append_new (json_history,
     851             :                                               json_pack ("{s:s, s:O, s:o, s:o, s:o, s:o, s:o, s:o}",
     852             :                                                          "type", "CLOSING",
     853             :                                                          "receiver_account_details", pos->details.closing->receiver_account_details,
     854             :                                                          "wtid", GNUNET_JSON_from_data_auto (&pos->details.closing->wtid),
     855             :                                                          "exchange_pub", GNUNET_JSON_from_data_auto (&pub),
     856             :                                                          "exchange_sig", GNUNET_JSON_from_data_auto (&sig),
     857             :                                                          "timestamp", GNUNET_JSON_from_time_abs (pos->details.closing->execution_date),
     858             :                                                          "amount", TALER_JSON_from_amount (&value),
     859             :                                                          "closing_fee", TALER_JSON_from_amount (&pos->details.closing->closing_fee))));
     860             :       }
     861           0 :       break;
     862             :     }
     863             :   }
     864           3 :   if (0 == (1 & ret))
     865             :   {
     866           0 :     GNUNET_break (0);
     867           0 :     json_decref (json_history);
     868           0 :     return NULL;
     869             :   }
     870           3 :   if (0 == (2 & ret))
     871             :   {
     872             :     /* did not encounter any withdraw operations, set to zero */
     873           0 :     TALER_amount_get_zero (deposit_total.currency,
     874             :                            &withdraw_total);
     875             :   }
     876           3 :   if (GNUNET_SYSERR ==
     877           3 :       TALER_amount_subtract (balance,
     878             :                              &deposit_total,
     879             :                              &withdraw_total))
     880             :   {
     881           0 :     GNUNET_break (0);
     882           0 :     json_decref (json_history);
     883           0 :     return NULL;
     884             :   }
     885             : 
     886           3 :   return json_history;
     887             : }
     888             : 
     889             : 
     890             : /**
     891             :  * A merchant asked for details about a deposit, but
     892             :  * we do not know anything about the deposit. Generate the
     893             :  * 404 reply.
     894             :  *
     895             :  * @param connection connection to the client
     896             :  * @param ec Taler error code
     897             :  * @return MHD result code
     898             :  */
     899             : int
     900           1 : TEH_RESPONSE_reply_transaction_unknown (struct MHD_Connection *connection,
     901             :                                         enum TALER_ErrorCode ec)
     902             : {
     903           1 :   return TEH_RESPONSE_reply_json_pack (connection,
     904             :                                        MHD_HTTP_NOT_FOUND,
     905             :                                        "{s:s, s:I}",
     906             :                                        "error", "Deposit unknown",
     907             :                                        "code", (json_int_t) ec);
     908             : }
     909             : 
     910             : 
     911             : /* end of taler-exchange-httpd_responses.c */

Generated by: LCOV version 1.13