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