merchant_api_get-private-fountains-FOUNTAIN_ID.c (8531B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General 16 Public License along with TALER; see the file COPYING.LGPL. 17 If not, see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file src/lib/merchant_api_get-private-fountains-FOUNTAIN_ID.c 21 * @brief Implementation of the GET /private/fountains/$FOUNTAIN_ID request 22 * @author Bohdan Potuzhnyi 23 */ 24 #include "platform.h" 25 #include <curl/curl.h> 26 #include <jansson.h> 27 #include <microhttpd.h> /* just for HTTP status codes */ 28 #include <gnunet/gnunet_util_lib.h> 29 #include <gnunet/gnunet_curl_lib.h> 30 #include <taler/merchant/get-private-fountains-FOUNTAIN_ID.h> 31 #include "merchant_api_curl_defaults.h" 32 #include "merchant_api_common.h" 33 #include <taler/taler_json_lib.h> 34 #include <taler/taler_curl_lib.h> 35 36 37 /** 38 * Handle for a GET /private/fountains/$FOUNTAIN_ID operation. 39 */ 40 struct TALER_MERCHANT_GetPrivateFountainHandle 41 { 42 /** 43 * Base URL of the merchant backend. 44 */ 45 char *base_url; 46 47 /** 48 * The full URL for this request. 49 */ 50 char *url; 51 52 /** 53 * Fountain identifier. 54 */ 55 char *fountain_id; 56 57 /** 58 * Handle for the request. 59 */ 60 struct GNUNET_CURL_Job *job; 61 62 /** 63 * Function to call with the result. 64 */ 65 TALER_MERCHANT_GetPrivateFountainCallback cb; 66 67 /** 68 * Closure for @a cb. 69 */ 70 TALER_MERCHANT_GET_PRIVATE_FOUNTAIN_RESULT_CLOSURE *cb_cls; 71 72 /** 73 * Reference to the execution context. 74 */ 75 struct GNUNET_CURL_Context *ctx; 76 }; 77 78 79 /** 80 * Parse the fountain details and invoke the callback. 81 * 82 * @param gpfh operation handle 83 * @param[in,out] gfr response to complete and pass to the callback 84 */ 85 static void 86 handle_ok (struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh, 87 struct TALER_MERCHANT_GetPrivateFountainResponse *gfr) 88 { 89 const json_t *jgrants; 90 struct GNUNET_JSON_Specification spec[] = { 91 GNUNET_JSON_spec_string ("description", 92 &gfr->details.ok.description), 93 GNUNET_JSON_spec_relative_time ("poll_freq", 94 &gfr->details.ok.poll_freq), 95 GNUNET_JSON_spec_array_const ("grants", 96 &jgrants), 97 GNUNET_JSON_spec_end () 98 }; 99 100 if (GNUNET_OK != 101 GNUNET_JSON_parse (gfr->hr.reply, 102 spec, 103 NULL, 104 NULL)) 105 { 106 GNUNET_break_op (0); 107 gfr->hr.http_status = 0; 108 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 109 gpfh->cb (gpfh->cb_cls, 110 gfr); 111 return; 112 } 113 if (json_array_size (jgrants) > TALER_MERCHANT_MAX_FOUNTAIN_RESPONSE_ITEMS) 114 { 115 gfr->hr.http_status = 0; 116 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 117 gpfh->cb (gpfh->cb_cls, 118 gfr); 119 return; 120 } 121 { 122 unsigned int len = (unsigned int) json_array_size (jgrants); 123 struct TALER_MERCHANT_FountainGrant *grants 124 = GNUNET_new_array (GNUNET_NZL (len), 125 struct TALER_MERCHANT_FountainGrant); 126 size_t idx; 127 json_t *jg; 128 129 json_array_foreach ((json_t *) jgrants, idx, jg) 130 { 131 struct TALER_MERCHANT_FountainGrant *fg = &grants[idx]; 132 uint32_t window; 133 struct GNUNET_JSON_Specification ispec[] = { 134 GNUNET_JSON_spec_string ("token_family_slug", 135 &fg->token_family_slug), 136 GNUNET_JSON_spec_uint64 ("tokens_per_period_limit", 137 &fg->tokens_per_period_limit), 138 GNUNET_JSON_spec_uint64 ("tokens_per_period_stash", 139 &fg->tokens_per_period_stash), 140 GNUNET_JSON_spec_uint32 ("key_window_size", 141 &window), 142 GNUNET_JSON_spec_end () 143 }; 144 145 if (GNUNET_OK != 146 GNUNET_JSON_parse (jg, 147 ispec, 148 NULL, 149 NULL)) 150 { 151 GNUNET_break_op (0); 152 gfr->hr.http_status = 0; 153 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 154 GNUNET_free (grants); 155 gpfh->cb (gpfh->cb_cls, 156 gfr); 157 return; 158 } 159 fg->key_window_size = window; 160 } 161 gfr->details.ok.grants = grants; 162 gfr->details.ok.grants_len = len; 163 gpfh->cb (gpfh->cb_cls, 164 gfr); 165 GNUNET_free (grants); 166 } 167 } 168 169 170 /** 171 * Function called when we're done processing the 172 * HTTP GET /private/fountains/$FOUNTAIN_ID request. 173 * 174 * @param cls the `struct TALER_MERCHANT_GetPrivateFountainHandle` 175 * @param response_code HTTP response code, 0 on error 176 * @param response response body, NULL if not in JSON 177 */ 178 static void 179 handle_get_fountain_finished (void *cls, 180 long response_code, 181 const void *response) 182 { 183 struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh = cls; 184 const json_t *json = response; 185 struct TALER_MERCHANT_GetPrivateFountainResponse gfr = { 186 .hr.http_status = (unsigned int) response_code, 187 .hr.reply = json 188 }; 189 190 gpfh->job = NULL; 191 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 192 "GET /private/fountains/$ID completed with response code %u\n", 193 (unsigned int) response_code); 194 switch (response_code) 195 { 196 case 0: 197 gfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 198 break; 199 case MHD_HTTP_OK: 200 handle_ok (gpfh, 201 &gfr); 202 TALER_MERCHANT_get_private_fountain_cancel (gpfh); 203 return; 204 case MHD_HTTP_UNAUTHORIZED: 205 case MHD_HTTP_FORBIDDEN: 206 case MHD_HTTP_NOT_FOUND: 207 case MHD_HTTP_INTERNAL_SERVER_ERROR: 208 gfr.hr.ec = TALER_JSON_get_error_code (json); 209 gfr.hr.hint = TALER_JSON_get_error_hint (json); 210 break; 211 default: 212 TALER_MERCHANT_parse_error_details_ (json, 213 response_code, 214 &gfr.hr); 215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 216 "Unexpected response code %u/%d\n", 217 (unsigned int) response_code, 218 (int) gfr.hr.ec); 219 GNUNET_break_op (0); 220 break; 221 } 222 gpfh->cb (gpfh->cb_cls, 223 &gfr); 224 TALER_MERCHANT_get_private_fountain_cancel (gpfh); 225 } 226 227 228 struct TALER_MERCHANT_GetPrivateFountainHandle * 229 TALER_MERCHANT_get_private_fountain_create ( 230 struct GNUNET_CURL_Context *ctx, 231 const char *url, 232 const char *fountain_id) 233 { 234 struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh; 235 236 gpfh = GNUNET_new (struct TALER_MERCHANT_GetPrivateFountainHandle); 237 gpfh->ctx = ctx; 238 gpfh->base_url = GNUNET_strdup (url); 239 gpfh->fountain_id = GNUNET_strdup (fountain_id); 240 return gpfh; 241 } 242 243 244 enum TALER_ErrorCode 245 TALER_MERCHANT_get_private_fountain_start ( 246 struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh, 247 TALER_MERCHANT_GetPrivateFountainCallback cb, 248 TALER_MERCHANT_GET_PRIVATE_FOUNTAIN_RESULT_CLOSURE *cb_cls) 249 { 250 CURL *eh; 251 252 gpfh->cb = cb; 253 gpfh->cb_cls = cb_cls; 254 { 255 char *path; 256 257 GNUNET_asprintf (&path, 258 "private/fountains/%s", 259 gpfh->fountain_id); 260 gpfh->url = TALER_url_join (gpfh->base_url, 261 path, 262 NULL); 263 GNUNET_free (path); 264 } 265 if (NULL == gpfh->url) 266 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 267 eh = TALER_MERCHANT_curl_easy_get_ (gpfh->url); 268 if (NULL == eh) 269 { 270 GNUNET_break (0); 271 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 272 } 273 gpfh->job = GNUNET_CURL_job_add (gpfh->ctx, 274 eh, 275 &handle_get_fountain_finished, 276 gpfh); 277 if (NULL == gpfh->job) 278 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 279 return TALER_EC_NONE; 280 } 281 282 283 void 284 TALER_MERCHANT_get_private_fountain_cancel ( 285 struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh) 286 { 287 if (NULL != gpfh->job) 288 { 289 GNUNET_CURL_job_cancel (gpfh->job); 290 gpfh->job = NULL; 291 } 292 GNUNET_free (gpfh->fountain_id); 293 GNUNET_free (gpfh->url); 294 GNUNET_free (gpfh->base_url); 295 GNUNET_free (gpfh); 296 } 297 298 299 /* end of merchant_api_get-private-fountains-FOUNTAIN_ID.c */