Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2022 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-exchange-httpd_reserves_status.c
18 : * @brief Handle /reserves/$RESERVE_PUB STATUS requests
19 : * @author Florian Dold
20 : * @author Benedikt Mueller
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h"
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <jansson.h>
26 : #include "taler_mhd_lib.h"
27 : #include "taler_json_lib.h"
28 : #include "taler_dbevents.h"
29 : #include "taler-exchange-httpd_keys.h"
30 : #include "taler-exchange-httpd_reserves_status.h"
31 : #include "taler-exchange-httpd_responses.h"
32 :
33 : /**
34 : * How far do we allow a client's time to be off when
35 : * checking the request timestamp?
36 : */
37 : #define TIMESTAMP_TOLERANCE \
38 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
39 :
40 :
41 : /**
42 : * Closure for #reserve_status_transaction.
43 : */
44 : struct ReserveStatusContext
45 : {
46 : /**
47 : * Public key of the reserve the inquiry is about.
48 : */
49 : const struct TALER_ReservePublicKeyP *reserve_pub;
50 :
51 : /**
52 : * History of the reserve, set in the callback.
53 : */
54 : struct TALER_EXCHANGEDB_ReserveHistory *rh;
55 :
56 : /**
57 : * Sum of incoming transactions within the returned history.
58 : * (currently not used).
59 : */
60 : struct TALER_Amount balance_in;
61 :
62 : /**
63 : * Sum of outgoing transactions within the returned history.
64 : * (currently not used).
65 : */
66 : struct TALER_Amount balance_out;
67 :
68 : /**
69 : * Current reserve balance.
70 : */
71 : struct TALER_Amount balance;
72 : };
73 :
74 :
75 : /**
76 : * Send reserve status to client.
77 : *
78 : * @param connection connection to the client
79 : * @param rhc reserve history to return
80 : * @return MHD result code
81 : */
82 : static MHD_RESULT
83 0 : reply_reserve_status_success (struct MHD_Connection *connection,
84 : const struct ReserveStatusContext *rhc)
85 : {
86 0 : const struct TALER_EXCHANGEDB_ReserveHistory *rh = rhc->rh;
87 : json_t *json_history;
88 :
89 0 : json_history = TEH_RESPONSE_compile_reserve_history (rh);
90 0 : if (NULL == json_history)
91 0 : return TALER_MHD_reply_with_error (connection,
92 : MHD_HTTP_INTERNAL_SERVER_ERROR,
93 : TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
94 : NULL);
95 0 : return TALER_MHD_REPLY_JSON_PACK (
96 : connection,
97 : MHD_HTTP_OK,
98 : TALER_JSON_pack_amount ("balance",
99 : &rhc->balance),
100 : GNUNET_JSON_pack_array_steal ("history",
101 : json_history));
102 : }
103 :
104 :
105 : /**
106 : * Function implementing /reserves/ STATUS transaction.
107 : * Execute a /reserves/ STATUS. Given the public key of a reserve,
108 : * return the associated transaction history. Runs the
109 : * transaction logic; IF it returns a non-error code, the transaction
110 : * logic MUST NOT queue a MHD response. IF it returns an hard error,
111 : * the transaction logic MUST queue a MHD response and set @a mhd_ret.
112 : * IF it returns the soft error code, the function MAY be called again
113 : * to retry and MUST not queue a MHD response.
114 : *
115 : * @param cls a `struct ReserveStatusContext *`
116 : * @param connection MHD request which triggered the transaction
117 : * @param[out] mhd_ret set to MHD response status for @a connection,
118 : * if transaction failed (!); unused
119 : * @return transaction status
120 : */
121 : static enum GNUNET_DB_QueryStatus
122 0 : reserve_status_transaction (void *cls,
123 : struct MHD_Connection *connection,
124 : MHD_RESULT *mhd_ret)
125 : {
126 0 : struct ReserveStatusContext *rsc = cls;
127 : enum GNUNET_DB_QueryStatus qs;
128 :
129 0 : qs = TEH_plugin->get_reserve_status (TEH_plugin->cls,
130 : rsc->reserve_pub,
131 : &rsc->balance_in,
132 : &rsc->balance_out,
133 : &rsc->rh);
134 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
135 : {
136 0 : GNUNET_break (0);
137 : *mhd_ret
138 0 : = TALER_MHD_reply_with_error (connection,
139 : MHD_HTTP_INTERNAL_SERVER_ERROR,
140 : TALER_EC_GENERIC_DB_FETCH_FAILED,
141 : "get_reserve_status");
142 : }
143 0 : qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
144 : rsc->reserve_pub,
145 : &rsc->balance);
146 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
147 : {
148 0 : GNUNET_break (0);
149 : *mhd_ret
150 0 : = TALER_MHD_reply_with_error (connection,
151 : MHD_HTTP_INTERNAL_SERVER_ERROR,
152 : TALER_EC_GENERIC_DB_FETCH_FAILED,
153 : "get_reserve_balance");
154 : }
155 0 : return qs;
156 : }
157 :
158 :
159 : MHD_RESULT
160 0 : TEH_handler_reserves_status (struct TEH_RequestContext *rc,
161 : const struct TALER_ReservePublicKeyP *reserve_pub,
162 : const json_t *root)
163 : {
164 : struct ReserveStatusContext rsc;
165 : MHD_RESULT mhd_ret;
166 : struct GNUNET_TIME_Timestamp timestamp;
167 : struct TALER_ReserveSignatureP reserve_sig;
168 : struct GNUNET_JSON_Specification spec[] = {
169 0 : GNUNET_JSON_spec_timestamp ("request_timestamp",
170 : ×tamp),
171 0 : GNUNET_JSON_spec_fixed_auto ("reserve_sig",
172 : &reserve_sig),
173 0 : GNUNET_JSON_spec_end ()
174 : };
175 : struct GNUNET_TIME_Timestamp now;
176 :
177 0 : rsc.reserve_pub = reserve_pub;
178 : {
179 : enum GNUNET_GenericReturnValue res;
180 :
181 0 : res = TALER_MHD_parse_json_data (rc->connection,
182 : root,
183 : spec);
184 0 : if (GNUNET_SYSERR == res)
185 : {
186 0 : GNUNET_break (0);
187 0 : return MHD_NO; /* hard failure */
188 : }
189 0 : if (GNUNET_NO == res)
190 : {
191 0 : GNUNET_break_op (0);
192 0 : return MHD_YES; /* failure */
193 : }
194 : }
195 0 : now = GNUNET_TIME_timestamp_get ();
196 0 : if (! GNUNET_TIME_absolute_approx_eq (now.abs_time,
197 : timestamp.abs_time,
198 : TIMESTAMP_TOLERANCE))
199 : {
200 0 : GNUNET_break_op (0);
201 0 : return TALER_MHD_reply_with_error (rc->connection,
202 : MHD_HTTP_BAD_REQUEST,
203 : TALER_EC_EXCHANGE_GENERIC_CLOCK_SKEW,
204 : NULL);
205 : }
206 0 : if (GNUNET_OK !=
207 0 : TALER_wallet_reserve_status_verify (timestamp,
208 : reserve_pub,
209 : &reserve_sig))
210 : {
211 0 : GNUNET_break_op (0);
212 0 : return TALER_MHD_reply_with_error (rc->connection,
213 : MHD_HTTP_FORBIDDEN,
214 : TALER_EC_EXCHANGE_RESERVES_STATUS_BAD_SIGNATURE,
215 : NULL);
216 : }
217 0 : rsc.rh = NULL;
218 0 : if (GNUNET_OK !=
219 0 : TEH_DB_run_transaction (rc->connection,
220 : "get reserve status",
221 : TEH_MT_REQUEST_OTHER,
222 : &mhd_ret,
223 : &reserve_status_transaction,
224 : &rsc))
225 : {
226 0 : return mhd_ret;
227 : }
228 0 : if (NULL == rsc.rh)
229 : {
230 0 : return TALER_MHD_reply_with_error (rc->connection,
231 : MHD_HTTP_NOT_FOUND,
232 : TALER_EC_EXCHANGE_RESERVES_STATUS_UNKNOWN,
233 : NULL);
234 : }
235 0 : mhd_ret = reply_reserve_status_success (rc->connection,
236 : &rsc);
237 0 : TEH_plugin->free_reserve_history (TEH_plugin->cls,
238 : rsc.rh);
239 0 : return mhd_ret;
240 : }
241 :
242 :
243 : /* end of taler-exchange-httpd_reserves_status.c */
|