exchange_api_get-reserves-attest-RESERVE_PUB.c (8268B)
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-attest-RESERVE_PUB.c 19 * @brief Implementation of the GET /reserves/$RESERVE_PUB/attest request 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/attest Handle 37 */ 38 struct TALER_EXCHANGE_GetReservesAttestHandle 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_GetReservesAttestCallback cb; 65 66 /** 67 * Closure for @e cb. 68 */ 69 TALER_EXCHANGE_GET_RESERVES_ATTEST_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 78 79 /** 80 * We received an #MHD_HTTP_OK status code. Handle the JSON response. 81 * 82 * @param grah handle of the request 83 * @param j JSON response 84 * @return #GNUNET_OK on success 85 */ 86 static enum GNUNET_GenericReturnValue 87 handle_reserves_get_attestable_ok ( 88 struct TALER_EXCHANGE_GetReservesAttestHandle *grah, 89 const json_t *j) 90 { 91 struct TALER_EXCHANGE_GetReservesAttestResponse rs = { 92 .hr.reply = j, 93 .hr.http_status = MHD_HTTP_OK 94 }; 95 const json_t *details; 96 struct GNUNET_JSON_Specification spec[] = { 97 GNUNET_JSON_spec_array_const ("details", 98 &details), 99 GNUNET_JSON_spec_end () 100 }; 101 102 if (GNUNET_OK != 103 GNUNET_JSON_parse (j, 104 spec, 105 NULL, 106 NULL)) 107 { 108 GNUNET_break_op (0); 109 return GNUNET_SYSERR; 110 } 111 { 112 size_t dlen = json_array_size (details); 113 const char *attributes[GNUNET_NZL (dlen)]; 114 115 for (unsigned int i = 0; i < dlen; i++) 116 { 117 json_t *detail = json_array_get (details, 118 i); 119 attributes[i] = json_string_value (detail); 120 if (NULL == attributes[i]) 121 { 122 GNUNET_break_op (0); 123 return GNUNET_SYSERR; 124 } 125 } 126 rs.details.ok.attributes_length = dlen; 127 rs.details.ok.attributes = attributes; 128 grah->cb (grah->cb_cls, 129 &rs); 130 grah->cb = NULL; 131 } 132 return GNUNET_OK; 133 } 134 135 136 /** 137 * Function called when we're done processing the 138 * HTTP GET /reserves/$RESERVE_PUB/attest request. 139 * 140 * @param cls the `struct TALER_EXCHANGE_GetReservesAttestHandle` 141 * @param response_code HTTP response code, 0 on error 142 * @param response parsed JSON result, NULL on error 143 */ 144 static void 145 handle_reserves_get_attestable_finished (void *cls, 146 long response_code, 147 const void *response) 148 { 149 struct TALER_EXCHANGE_GetReservesAttestHandle *grah = cls; 150 const json_t *j = response; 151 struct TALER_EXCHANGE_GetReservesAttestResponse rs = { 152 .hr.reply = j, 153 .hr.http_status = (unsigned int) response_code 154 }; 155 156 grah->job = NULL; 157 switch (response_code) 158 { 159 case 0: 160 rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 161 break; 162 case MHD_HTTP_OK: 163 if (GNUNET_OK != 164 handle_reserves_get_attestable_ok (grah, 165 j)) 166 { 167 rs.hr.http_status = 0; 168 rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 169 } 170 break; 171 case MHD_HTTP_BAD_REQUEST: 172 /* This should never happen, either us or the exchange is buggy 173 (or API version conflict); just pass 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_NOT_FOUND: 178 /* Nothing really to verify, this should never 179 happen, we should pass the JSON reply 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 case MHD_HTTP_CONFLICT: 184 /* Nothing really to verify, this should never 185 happen, we should pass the JSON reply to the application */ 186 rs.hr.ec = TALER_JSON_get_error_code (j); 187 rs.hr.hint = TALER_JSON_get_error_hint (j); 188 break; 189 case MHD_HTTP_INTERNAL_SERVER_ERROR: 190 /* Server had an internal issue; we should retry, but this API 191 leaves this to the application */ 192 rs.hr.ec = TALER_JSON_get_error_code (j); 193 rs.hr.hint = TALER_JSON_get_error_hint (j); 194 break; 195 default: 196 /* unexpected response code */ 197 GNUNET_break_op (0); 198 rs.hr.ec = TALER_JSON_get_error_code (j); 199 rs.hr.hint = TALER_JSON_get_error_hint (j); 200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 201 "Unexpected response code %u/%d for reserves get_attestable\n", 202 (unsigned int) response_code, 203 (int) rs.hr.ec); 204 break; 205 } 206 if (NULL != grah->cb) 207 { 208 grah->cb (grah->cb_cls, 209 &rs); 210 grah->cb = NULL; 211 } 212 TALER_EXCHANGE_get_reserves_attest_cancel (grah); 213 } 214 215 216 struct TALER_EXCHANGE_GetReservesAttestHandle * 217 TALER_EXCHANGE_get_reserves_attest_create ( 218 struct GNUNET_CURL_Context *ctx, 219 const char *url, 220 const struct TALER_ReservePublicKeyP *reserve_pub) 221 { 222 struct TALER_EXCHANGE_GetReservesAttestHandle *grah; 223 224 grah = GNUNET_new (struct TALER_EXCHANGE_GetReservesAttestHandle); 225 grah->ctx = ctx; 226 grah->base_url = GNUNET_strdup (url); 227 grah->reserve_pub = *reserve_pub; 228 return grah; 229 } 230 231 232 enum TALER_ErrorCode 233 TALER_EXCHANGE_get_reserves_attest_start ( 234 struct TALER_EXCHANGE_GetReservesAttestHandle *grah, 235 TALER_EXCHANGE_GetReservesAttestCallback cb, 236 TALER_EXCHANGE_GET_RESERVES_ATTEST_RESULT_CLOSURE *cb_cls) 237 { 238 char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32]; 239 CURL *eh; 240 241 if (NULL != grah->job) 242 { 243 GNUNET_break (0); 244 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 245 } 246 grah->cb = cb; 247 grah->cb_cls = cb_cls; 248 { 249 char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2]; 250 char *end; 251 252 end = GNUNET_STRINGS_data_to_string ( 253 &grah->reserve_pub, 254 sizeof (grah->reserve_pub), 255 pub_str, 256 sizeof (pub_str)); 257 *end = '\0'; 258 GNUNET_snprintf (arg_str, 259 sizeof (arg_str), 260 "reserves-attest/%s", 261 pub_str); 262 } 263 grah->url = TALER_url_join (grah->base_url, 264 arg_str, 265 NULL); 266 if (NULL == grah->url) 267 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 268 eh = TALER_EXCHANGE_curl_easy_get_ (grah->url); 269 if (NULL == eh) 270 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 271 grah->job = GNUNET_CURL_job_add (grah->ctx, 272 eh, 273 &handle_reserves_get_attestable_finished, 274 grah); 275 if (NULL == grah->job) 276 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 277 return TALER_EC_NONE; 278 } 279 280 281 void 282 TALER_EXCHANGE_get_reserves_attest_cancel ( 283 struct TALER_EXCHANGE_GetReservesAttestHandle *grah) 284 { 285 if (NULL != grah->job) 286 { 287 GNUNET_CURL_job_cancel (grah->job); 288 grah->job = NULL; 289 } 290 GNUNET_free (grah->url); 291 GNUNET_free (grah->base_url); 292 GNUNET_free (grah); 293 } 294 295 296 /* end of exchange_api_get-reserves-attest-RESERVE_PUB.c */