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 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 General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file lib/exchange_api_reserves_get.c
19 : * @brief Implementation of the GET /reserves/$RESERVE_PUB requests
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include <jansson.h>
24 : #include <microhttpd.h> /* just for HTTP status codes */
25 : #include <gnunet/gnunet_util_lib.h>
26 : #include <gnunet/gnunet_json_lib.h>
27 : #include <gnunet/gnunet_curl_lib.h>
28 : #include "taler_exchange_service.h"
29 : #include "taler_json_lib.h"
30 : #include "exchange_api_handle.h"
31 : #include "taler_signatures.h"
32 : #include "exchange_api_curl_defaults.h"
33 :
34 :
35 : /**
36 : * @brief A /reserves/ GET Handle
37 : */
38 : struct TALER_EXCHANGE_ReservesGetHandle
39 : {
40 :
41 : /**
42 : * The url for this request.
43 : */
44 : char *url;
45 :
46 : /**
47 : * Handle for the request.
48 : */
49 : struct GNUNET_CURL_Job *job;
50 :
51 : /**
52 : * Function to call with the result.
53 : */
54 : TALER_EXCHANGE_ReservesGetCallback cb;
55 :
56 : /**
57 : * Public key of the reserve we are querying.
58 : */
59 : struct TALER_ReservePublicKeyP reserve_pub;
60 :
61 : /**
62 : * Closure for @a cb.
63 : */
64 : void *cb_cls;
65 :
66 : };
67 :
68 :
69 : /**
70 : * We received an #MHD_HTTP_OK status code. Handle the JSON
71 : * response.
72 : *
73 : * @param rgh handle of the request
74 : * @param j JSON response
75 : * @return #GNUNET_OK on success
76 : */
77 : static enum GNUNET_GenericReturnValue
78 30 : handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
79 : const json_t *j)
80 : {
81 30 : struct TALER_EXCHANGE_ReserveSummary rs = {
82 : .hr.reply = j,
83 : .hr.http_status = MHD_HTTP_OK
84 : };
85 : struct GNUNET_JSON_Specification spec[] = {
86 30 : TALER_JSON_spec_amount_any ("balance",
87 : &rs.details.ok.balance),
88 30 : GNUNET_JSON_spec_mark_optional (
89 : GNUNET_JSON_spec_string (
90 : "last_origin",
91 : (const char **) &rs.details.ok.last_origin.full_payto),
92 : NULL),
93 30 : GNUNET_JSON_spec_end ()
94 : };
95 :
96 30 : if (GNUNET_OK !=
97 30 : GNUNET_JSON_parse (j,
98 : spec,
99 : NULL,
100 : NULL))
101 : {
102 0 : GNUNET_break_op (0);
103 0 : return GNUNET_SYSERR;
104 : }
105 30 : rgh->cb (rgh->cb_cls,
106 : &rs);
107 30 : rgh->cb = NULL;
108 30 : return GNUNET_OK;
109 : }
110 :
111 :
112 : /**
113 : * Function called when we're done processing the
114 : * HTTP /reserves/ GET request.
115 : *
116 : * @param cls the `struct TALER_EXCHANGE_ReservesGetHandle`
117 : * @param response_code HTTP response code, 0 on error
118 : * @param response parsed JSON result, NULL on error
119 : */
120 : static void
121 30 : handle_reserves_get_finished (void *cls,
122 : long response_code,
123 : const void *response)
124 : {
125 30 : struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls;
126 30 : const json_t *j = response;
127 30 : struct TALER_EXCHANGE_ReserveSummary rs = {
128 : .hr.reply = j,
129 30 : .hr.http_status = (unsigned int) response_code
130 : };
131 :
132 30 : rgh->job = NULL;
133 30 : switch (response_code)
134 : {
135 0 : case 0:
136 0 : rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
137 0 : break;
138 30 : case MHD_HTTP_OK:
139 30 : if (GNUNET_OK !=
140 30 : handle_reserves_get_ok (rgh,
141 : j))
142 : {
143 0 : rs.hr.http_status = 0;
144 0 : rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
145 : }
146 30 : break;
147 0 : case MHD_HTTP_BAD_REQUEST:
148 : /* This should never happen, either us or the exchange is buggy
149 : (or API version conflict); just pass JSON reply to the application */
150 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
151 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
152 0 : break;
153 0 : case MHD_HTTP_NOT_FOUND:
154 : /* Nothing really to verify, this should never
155 : happen, we should pass the JSON reply to the application */
156 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
157 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
158 0 : break;
159 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
160 : /* Server had an internal issue; we should retry, but this API
161 : leaves this to the application */
162 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
163 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
164 0 : break;
165 0 : default:
166 : /* unexpected response code */
167 0 : GNUNET_break_op (0);
168 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
169 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
170 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
171 : "Unexpected response code %u/%d for GET %s\n",
172 : (unsigned int) response_code,
173 : (int) rs.hr.ec,
174 : rgh->url);
175 0 : break;
176 : }
177 30 : if (NULL != rgh->cb)
178 : {
179 0 : rgh->cb (rgh->cb_cls,
180 : &rs);
181 0 : rgh->cb = NULL;
182 : }
183 30 : TALER_EXCHANGE_reserves_get_cancel (rgh);
184 30 : }
185 :
186 :
187 : struct TALER_EXCHANGE_ReservesGetHandle *
188 30 : TALER_EXCHANGE_reserves_get (
189 : struct GNUNET_CURL_Context *ctx,
190 : const char *url,
191 : const struct TALER_ReservePublicKeyP *reserve_pub,
192 : struct GNUNET_TIME_Relative timeout,
193 : TALER_EXCHANGE_ReservesGetCallback cb,
194 : void *cb_cls)
195 : {
196 : struct TALER_EXCHANGE_ReservesGetHandle *rgh;
197 : CURL *eh;
198 : char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 16 + 32];
199 30 : unsigned int tms
200 30 : = (unsigned int) timeout.rel_value_us
201 30 : / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
202 :
203 : {
204 : char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
205 : char *end;
206 : char timeout_str[32];
207 :
208 30 : end = GNUNET_STRINGS_data_to_string (
209 : reserve_pub,
210 : sizeof (*reserve_pub),
211 : pub_str,
212 : sizeof (pub_str));
213 30 : *end = '\0';
214 30 : GNUNET_snprintf (timeout_str,
215 : sizeof (timeout_str),
216 : "%u",
217 : tms);
218 30 : if (0 == tms)
219 23 : GNUNET_snprintf (arg_str,
220 : sizeof (arg_str),
221 : "reserves/%s",
222 : pub_str);
223 : else
224 7 : GNUNET_snprintf (arg_str,
225 : sizeof (arg_str),
226 : "reserves/%s?timeout_ms=%s",
227 : pub_str,
228 : timeout_str);
229 : }
230 30 : rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesGetHandle);
231 30 : rgh->cb = cb;
232 30 : rgh->cb_cls = cb_cls;
233 30 : rgh->reserve_pub = *reserve_pub;
234 30 : rgh->url = TALER_url_join (url,
235 : arg_str,
236 : NULL);
237 30 : if (NULL == rgh->url)
238 : {
239 0 : GNUNET_free (rgh);
240 0 : return NULL;
241 : }
242 30 : eh = TALER_EXCHANGE_curl_easy_get_ (rgh->url);
243 30 : if (NULL == eh)
244 : {
245 0 : GNUNET_break (0);
246 0 : GNUNET_free (rgh->url);
247 0 : GNUNET_free (rgh);
248 0 : return NULL;
249 : }
250 30 : if (0 != tms)
251 : {
252 7 : GNUNET_break (CURLE_OK ==
253 : curl_easy_setopt (eh,
254 : CURLOPT_TIMEOUT_MS,
255 : (long) (tms + 100L)));
256 : }
257 30 : rgh->job = GNUNET_CURL_job_add (ctx,
258 : eh,
259 : &handle_reserves_get_finished,
260 : rgh);
261 30 : return rgh;
262 : }
263 :
264 :
265 : void
266 30 : TALER_EXCHANGE_reserves_get_cancel (
267 : struct TALER_EXCHANGE_ReservesGetHandle *rgh)
268 : {
269 30 : if (NULL != rgh->job)
270 : {
271 0 : GNUNET_CURL_job_cancel (rgh->job);
272 0 : rgh->job = NULL;
273 : }
274 30 : GNUNET_free (rgh->url);
275 30 : GNUNET_free (rgh);
276 30 : }
277 :
278 :
279 : /* end of exchange_api_reserves_get.c */
|