exchange_api_reserves_get.c (7500B)
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 "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 /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 handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh, 79 const json_t *j) 80 { 81 struct TALER_EXCHANGE_ReserveSummary rs = { 82 .hr.reply = j, 83 .hr.http_status = MHD_HTTP_OK 84 }; 85 struct GNUNET_JSON_Specification spec[] = { 86 TALER_JSON_spec_amount_any ("balance", 87 &rs.details.ok.balance), 88 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 GNUNET_JSON_spec_end () 94 }; 95 96 if (GNUNET_OK != 97 GNUNET_JSON_parse (j, 98 spec, 99 NULL, 100 NULL)) 101 { 102 GNUNET_break_op (0); 103 return GNUNET_SYSERR; 104 } 105 rgh->cb (rgh->cb_cls, 106 &rs); 107 rgh->cb = NULL; 108 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 handle_reserves_get_finished (void *cls, 122 long response_code, 123 const void *response) 124 { 125 struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls; 126 const json_t *j = response; 127 struct TALER_EXCHANGE_ReserveSummary rs = { 128 .hr.reply = j, 129 .hr.http_status = (unsigned int) response_code 130 }; 131 132 rgh->job = NULL; 133 switch (response_code) 134 { 135 case 0: 136 rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 137 break; 138 case MHD_HTTP_OK: 139 if (GNUNET_OK != 140 handle_reserves_get_ok (rgh, 141 j)) 142 { 143 rs.hr.http_status = 0; 144 rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 145 } 146 break; 147 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 rs.hr.ec = TALER_JSON_get_error_code (j); 151 rs.hr.hint = TALER_JSON_get_error_hint (j); 152 break; 153 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 rs.hr.ec = TALER_JSON_get_error_code (j); 157 rs.hr.hint = TALER_JSON_get_error_hint (j); 158 break; 159 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 rs.hr.ec = TALER_JSON_get_error_code (j); 163 rs.hr.hint = TALER_JSON_get_error_hint (j); 164 break; 165 default: 166 /* unexpected response code */ 167 GNUNET_break_op (0); 168 rs.hr.ec = TALER_JSON_get_error_code (j); 169 rs.hr.hint = TALER_JSON_get_error_hint (j); 170 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 break; 176 } 177 if (NULL != rgh->cb) 178 { 179 rgh->cb (rgh->cb_cls, 180 &rs); 181 rgh->cb = NULL; 182 } 183 TALER_EXCHANGE_reserves_get_cancel (rgh); 184 } 185 186 187 struct TALER_EXCHANGE_ReservesGetHandle * 188 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 unsigned int tms 200 = (unsigned int) timeout.rel_value_us 201 / 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 end = GNUNET_STRINGS_data_to_string ( 209 reserve_pub, 210 sizeof (*reserve_pub), 211 pub_str, 212 sizeof (pub_str)); 213 *end = '\0'; 214 GNUNET_snprintf (timeout_str, 215 sizeof (timeout_str), 216 "%u", 217 tms); 218 if (0 == tms) 219 GNUNET_snprintf (arg_str, 220 sizeof (arg_str), 221 "reserves/%s", 222 pub_str); 223 else 224 GNUNET_snprintf (arg_str, 225 sizeof (arg_str), 226 "reserves/%s?timeout_ms=%s", 227 pub_str, 228 timeout_str); 229 } 230 rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesGetHandle); 231 rgh->cb = cb; 232 rgh->cb_cls = cb_cls; 233 rgh->reserve_pub = *reserve_pub; 234 rgh->url = TALER_url_join (url, 235 arg_str, 236 NULL); 237 if (NULL == rgh->url) 238 { 239 GNUNET_free (rgh); 240 return NULL; 241 } 242 eh = TALER_EXCHANGE_curl_easy_get_ (rgh->url); 243 if (NULL == eh) 244 { 245 GNUNET_break (0); 246 GNUNET_free (rgh->url); 247 GNUNET_free (rgh); 248 return NULL; 249 } 250 if (0 != tms) 251 { 252 GNUNET_break (CURLE_OK == 253 curl_easy_setopt (eh, 254 CURLOPT_TIMEOUT_MS, 255 (long) (tms + 100L))); 256 } 257 rgh->job = GNUNET_CURL_job_add (ctx, 258 eh, 259 &handle_reserves_get_finished, 260 rgh); 261 return rgh; 262 } 263 264 265 void 266 TALER_EXCHANGE_reserves_get_cancel ( 267 struct TALER_EXCHANGE_ReservesGetHandle *rgh) 268 { 269 if (NULL != rgh->job) 270 { 271 GNUNET_CURL_job_cancel (rgh->job); 272 rgh->job = NULL; 273 } 274 GNUNET_free (rgh->url); 275 GNUNET_free (rgh); 276 } 277 278 279 /* end of exchange_api_reserves_get.c */