exchange_api_get-reserves-RESERVE_PUB.c (8876B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2026 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_get-reserves-RESERVE_PUB.c 19 * @brief Implementation of the GET /reserves/$RESERVE_PUB requests 20 * @author Christian Grothoff 21 */ 22 #include "taler/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/taler_exchange_service.h" 29 #include "taler/taler_json_lib.h" 30 #include "exchange_api_handle.h" 31 #include "taler/taler_signatures.h" 32 #include "exchange_api_curl_defaults.h" 33 34 35 /** 36 * @brief A GET /reserves/$RESERVE_PUB Handle 37 */ 38 struct TALER_EXCHANGE_GetReservesHandle 39 { 40 41 /** 42 * Base URL of the exchange. 43 */ 44 char *base_url; 45 46 /** 47 * The url for this request. 48 */ 49 char *url; 50 51 /** 52 * CURL context to use. 53 */ 54 struct GNUNET_CURL_Context *ctx; 55 56 /** 57 * Handle for the request. 58 */ 59 struct GNUNET_CURL_Job *job; 60 61 /** 62 * Function to call with the result. 63 */ 64 TALER_EXCHANGE_GetReservesCallback cb; 65 66 /** 67 * Closure for @e cb. 68 */ 69 TALER_EXCHANGE_GET_RESERVES_RESULT_CLOSURE *cb_cls; 70 71 /** 72 * Public key of the reserve we are querying. 73 */ 74 struct TALER_ReservePublicKeyP reserve_pub; 75 76 /** 77 * Options for the request. 78 */ 79 struct 80 { 81 /** 82 * How long to wait for an answer (enables long polling). 83 */ 84 struct GNUNET_TIME_Relative timeout; 85 } options; 86 87 }; 88 89 90 /** 91 * We received an #MHD_HTTP_OK status code. Handle the JSON response. 92 * 93 * @param grh handle of the request 94 * @param j JSON response 95 * @return #GNUNET_OK on success 96 */ 97 static enum GNUNET_GenericReturnValue 98 handle_reserves_get_ok (struct TALER_EXCHANGE_GetReservesHandle *grh, 99 const json_t *j) 100 { 101 struct TALER_EXCHANGE_GetReservesResponse rs = { 102 .hr.reply = j, 103 .hr.http_status = MHD_HTTP_OK 104 }; 105 struct GNUNET_JSON_Specification spec[] = { 106 TALER_JSON_spec_amount_any ("balance", 107 &rs.details.ok.balance), 108 GNUNET_JSON_spec_mark_optional ( 109 GNUNET_JSON_spec_string ( 110 "last_origin", 111 (const char **) &rs.details.ok.last_origin.full_payto), 112 NULL), 113 GNUNET_JSON_spec_end () 114 }; 115 116 if (GNUNET_OK != 117 GNUNET_JSON_parse (j, 118 spec, 119 NULL, 120 NULL)) 121 { 122 GNUNET_break_op (0); 123 return GNUNET_SYSERR; 124 } 125 grh->cb (grh->cb_cls, 126 &rs); 127 grh->cb = NULL; 128 return GNUNET_OK; 129 } 130 131 132 /** 133 * Function called when we're done processing the 134 * HTTP GET /reserves/$RESERVE_PUB request. 135 * 136 * @param cls the `struct TALER_EXCHANGE_GetReservesHandle` 137 * @param response_code HTTP response code, 0 on error 138 * @param response parsed JSON result, NULL on error 139 */ 140 static void 141 handle_reserves_get_finished (void *cls, 142 long response_code, 143 const void *response) 144 { 145 struct TALER_EXCHANGE_GetReservesHandle *grh = cls; 146 const json_t *j = response; 147 struct TALER_EXCHANGE_GetReservesResponse rs = { 148 .hr.reply = j, 149 .hr.http_status = (unsigned int) response_code 150 }; 151 152 grh->job = NULL; 153 switch (response_code) 154 { 155 case 0: 156 rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 157 break; 158 case MHD_HTTP_OK: 159 if (GNUNET_OK != 160 handle_reserves_get_ok (grh, 161 j)) 162 { 163 rs.hr.http_status = 0; 164 rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 165 } 166 break; 167 case MHD_HTTP_BAD_REQUEST: 168 /* This should never happen, either us or the exchange is buggy 169 (or API version conflict); just pass JSON reply to the application */ 170 rs.hr.ec = TALER_JSON_get_error_code (j); 171 rs.hr.hint = TALER_JSON_get_error_hint (j); 172 break; 173 case MHD_HTTP_NOT_FOUND: 174 /* Nothing really to verify, this should never 175 happen, we should pass the JSON reply to the application */ 176 rs.hr.ec = TALER_JSON_get_error_code (j); 177 rs.hr.hint = TALER_JSON_get_error_hint (j); 178 break; 179 case MHD_HTTP_INTERNAL_SERVER_ERROR: 180 /* Server had an internal issue; we should retry, but this API 181 leaves this to the application */ 182 rs.hr.ec = TALER_JSON_get_error_code (j); 183 rs.hr.hint = TALER_JSON_get_error_hint (j); 184 break; 185 default: 186 /* unexpected response code */ 187 GNUNET_break_op (0); 188 rs.hr.ec = TALER_JSON_get_error_code (j); 189 rs.hr.hint = TALER_JSON_get_error_hint (j); 190 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 191 "Unexpected response code %u/%d for GET %s\n", 192 (unsigned int) response_code, 193 (int) rs.hr.ec, 194 grh->url); 195 break; 196 } 197 if (NULL != grh->cb) 198 { 199 grh->cb (grh->cb_cls, 200 &rs); 201 grh->cb = NULL; 202 } 203 TALER_EXCHANGE_get_reserves_cancel (grh); 204 } 205 206 207 struct TALER_EXCHANGE_GetReservesHandle * 208 TALER_EXCHANGE_get_reserves_create ( 209 struct GNUNET_CURL_Context *ctx, 210 const char *url, 211 const struct TALER_ReservePublicKeyP *reserve_pub) 212 { 213 struct TALER_EXCHANGE_GetReservesHandle *grh; 214 215 grh = GNUNET_new (struct TALER_EXCHANGE_GetReservesHandle); 216 grh->ctx = ctx; 217 grh->base_url = GNUNET_strdup (url); 218 grh->reserve_pub = *reserve_pub; 219 return grh; 220 } 221 222 223 enum GNUNET_GenericReturnValue 224 TALER_EXCHANGE_get_reserves_set_options_ ( 225 struct TALER_EXCHANGE_GetReservesHandle *grh, 226 unsigned int num_options, 227 const struct TALER_EXCHANGE_GetReservesOptionValue *options) 228 { 229 for (unsigned int i = 0; i < num_options; i++) 230 { 231 switch (options[i].option) 232 { 233 case TALER_EXCHANGE_GET_RESERVES_OPTION_END: 234 return GNUNET_OK; 235 case TALER_EXCHANGE_GET_RESERVES_OPTION_TIMEOUT: 236 grh->options.timeout = options[i].details.timeout; 237 break; 238 default: 239 GNUNET_break (0); 240 return GNUNET_SYSERR; 241 } 242 } 243 return GNUNET_OK; 244 } 245 246 247 enum TALER_ErrorCode 248 TALER_EXCHANGE_get_reserves_start ( 249 struct TALER_EXCHANGE_GetReservesHandle *grh, 250 TALER_EXCHANGE_GetReservesCallback cb, 251 TALER_EXCHANGE_GET_RESERVES_RESULT_CLOSURE *cb_cls) 252 { 253 char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 16]; 254 CURL *eh; 255 unsigned int tms 256 = (unsigned int) grh->options.timeout.rel_value_us 257 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; 258 259 if (NULL != grh->job) 260 { 261 GNUNET_break (0); 262 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 263 } 264 grh->cb = cb; 265 grh->cb_cls = cb_cls; 266 { 267 char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2]; 268 char *end; 269 char timeout_str[32]; 270 271 end = GNUNET_STRINGS_data_to_string ( 272 &grh->reserve_pub, 273 sizeof (grh->reserve_pub), 274 pub_str, 275 sizeof (pub_str)); 276 *end = '\0'; 277 GNUNET_snprintf (arg_str, 278 sizeof (arg_str), 279 "reserves/%s", 280 pub_str); 281 GNUNET_snprintf (timeout_str, 282 sizeof (timeout_str), 283 "%u", 284 tms); 285 grh->url = TALER_url_join (grh->base_url, 286 arg_str, 287 "timeout_ms", 288 (0 == tms) 289 ? NULL 290 : timeout_str, 291 NULL); 292 } 293 if (NULL == grh->url) 294 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 295 eh = TALER_EXCHANGE_curl_easy_get_ (grh->url); 296 if (NULL == eh) 297 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 298 if (0 != tms) 299 { 300 GNUNET_break (CURLE_OK == 301 curl_easy_setopt (eh, 302 CURLOPT_TIMEOUT_MS, 303 (long) (tms + 100L))); 304 } 305 grh->job = GNUNET_CURL_job_add (grh->ctx, 306 eh, 307 &handle_reserves_get_finished, 308 grh); 309 if (NULL == grh->job) 310 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 311 return TALER_EC_NONE; 312 } 313 314 315 void 316 TALER_EXCHANGE_get_reserves_cancel ( 317 struct TALER_EXCHANGE_GetReservesHandle *grh) 318 { 319 if (NULL != grh->job) 320 { 321 GNUNET_CURL_job_cancel (grh->job); 322 grh->job = NULL; 323 } 324 GNUNET_free (grh->url); 325 GNUNET_free (grh->base_url); 326 GNUNET_free (grh); 327 } 328 329 330 /* end of exchange_api_get-reserves-RESERVE_PUB.c */