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