merchant_api_get-private-fountains.c (8555B)
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.c 21 * @brief Implementation of the GET /private/fountains 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.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 operation. 39 */ 40 struct TALER_MERCHANT_GetPrivateFountainsHandle 41 { 42 /** 43 * Base URL of the merchant backend. 44 */ 45 char *base_url; 46 47 int64_t limit; 48 49 uint64_t offset; 50 51 /** 52 * The full URL for this request. 53 */ 54 char *url; 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_MERCHANT_GetPrivateFountainsCallback cb; 65 66 /** 67 * Closure for @a cb. 68 */ 69 TALER_MERCHANT_GET_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls; 70 71 /** 72 * Reference to the execution context. 73 */ 74 struct GNUNET_CURL_Context *ctx; 75 }; 76 77 78 /** 79 * Parse the "fountains" array and invoke the callback. 80 * 81 * @param gpfh operation handle 82 * @param[in,out] gfr response to complete and pass to the callback 83 */ 84 static void 85 handle_ok (struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh, 86 struct TALER_MERCHANT_GetPrivateFountainsResponse *gfr) 87 { 88 const json_t *jfountains; 89 struct GNUNET_JSON_Specification spec[] = { 90 GNUNET_JSON_spec_array_const ("fountains", 91 &jfountains), 92 GNUNET_JSON_spec_end () 93 }; 94 95 if (GNUNET_OK != 96 GNUNET_JSON_parse (gfr->hr.reply, 97 spec, 98 NULL, 99 NULL)) 100 { 101 GNUNET_break_op (0); 102 gfr->hr.http_status = 0; 103 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 104 gpfh->cb (gpfh->cb_cls, 105 gfr); 106 return; 107 } 108 /* Reject responses exceeding the server page cap before allocating. */ 109 if (json_array_size (jfountains) > 100) 110 { 111 gfr->hr.http_status = 0; 112 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 113 gpfh->cb (gpfh->cb_cls, 114 gfr); 115 return; 116 } 117 { 118 unsigned int len = (unsigned int) json_array_size (jfountains); 119 struct TALER_MERCHANT_FountainEntry *fountains 120 = GNUNET_new_array (GNUNET_NZL (len), 121 struct TALER_MERCHANT_FountainEntry); 122 size_t idx; 123 json_t *jf; 124 125 json_array_foreach ((json_t *) jfountains, idx, jf) 126 { 127 struct TALER_MERCHANT_FountainEntry *fe = &fountains[idx]; 128 struct GNUNET_JSON_Specification ispec[] = { 129 GNUNET_JSON_spec_uint64 ("fountain_serial", 130 &fe->fountain_serial), 131 GNUNET_JSON_spec_string ("fountain_id", 132 &fe->fountain_id), 133 GNUNET_JSON_spec_string ("description", 134 &fe->description), 135 GNUNET_JSON_spec_end () 136 }; 137 138 if (GNUNET_OK != 139 GNUNET_JSON_parse (jf, 140 ispec, 141 NULL, 142 NULL)) 143 { 144 GNUNET_break_op (0); 145 gfr->hr.http_status = 0; 146 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 147 GNUNET_free (fountains); 148 gpfh->cb (gpfh->cb_cls, 149 gfr); 150 return; 151 } 152 } 153 gfr->details.ok.fountains = fountains; 154 gfr->details.ok.fountains_len = len; 155 gpfh->cb (gpfh->cb_cls, 156 gfr); 157 GNUNET_free (fountains); 158 } 159 } 160 161 162 /** 163 * Function called when we're done processing the 164 * HTTP GET /private/fountains request. 165 * 166 * @param cls the `struct TALER_MERCHANT_GetPrivateFountainsHandle` 167 * @param response_code HTTP response code, 0 on error 168 * @param response response body, NULL if not in JSON 169 */ 170 static void 171 handle_get_fountains_finished (void *cls, 172 long response_code, 173 const void *response) 174 { 175 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh = cls; 176 const json_t *json = response; 177 struct TALER_MERCHANT_GetPrivateFountainsResponse gfr = { 178 .hr.http_status = (unsigned int) response_code, 179 .hr.reply = json 180 }; 181 182 gpfh->job = NULL; 183 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 184 "GET /private/fountains completed with response code %u\n", 185 (unsigned int) response_code); 186 switch (response_code) 187 { 188 case 0: 189 gfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 190 break; 191 case MHD_HTTP_OK: 192 handle_ok (gpfh, 193 &gfr); 194 TALER_MERCHANT_get_private_fountains_cancel (gpfh); 195 return; 196 case MHD_HTTP_UNAUTHORIZED: 197 case MHD_HTTP_FORBIDDEN: 198 case MHD_HTTP_NOT_FOUND: 199 case MHD_HTTP_INTERNAL_SERVER_ERROR: 200 gfr.hr.ec = TALER_JSON_get_error_code (json); 201 gfr.hr.hint = TALER_JSON_get_error_hint (json); 202 break; 203 default: 204 TALER_MERCHANT_parse_error_details_ (json, 205 response_code, 206 &gfr.hr); 207 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 208 "Unexpected response code %u/%d\n", 209 (unsigned int) response_code, 210 (int) gfr.hr.ec); 211 GNUNET_break_op (0); 212 break; 213 } 214 gpfh->cb (gpfh->cb_cls, 215 &gfr); 216 TALER_MERCHANT_get_private_fountains_cancel (gpfh); 217 } 218 219 220 struct TALER_MERCHANT_GetPrivateFountainsHandle * 221 TALER_MERCHANT_get_private_fountains_create ( 222 struct GNUNET_CURL_Context *ctx, 223 const char *url) 224 { 225 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh; 226 227 gpfh = GNUNET_new (struct TALER_MERCHANT_GetPrivateFountainsHandle); 228 gpfh->ctx = ctx; 229 gpfh->limit = 20; 230 gpfh->base_url = GNUNET_strdup (url); 231 return gpfh; 232 } 233 234 235 enum TALER_ErrorCode 236 TALER_MERCHANT_get_private_fountains_set_pagination ( 237 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh, 238 int64_t limit, 239 uint64_t offset) 240 { 241 if ( (offset > INT64_MAX) || (NULL != gpfh->job) ) 242 return TALER_EC_GENERIC_PARAMETER_MALFORMED; 243 gpfh->limit = limit; 244 gpfh->offset = offset; 245 return TALER_EC_NONE; 246 } 247 248 249 enum TALER_ErrorCode 250 TALER_MERCHANT_get_private_fountains_start ( 251 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh, 252 TALER_MERCHANT_GetPrivateFountainsCallback cb, 253 TALER_MERCHANT_GET_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls) 254 { 255 CURL *eh; 256 char limit_s[32]; 257 char offset_s[32]; 258 259 gpfh->cb = cb; 260 gpfh->cb_cls = cb_cls; 261 GNUNET_snprintf (limit_s, sizeof (limit_s), "%lld", 262 (long long) gpfh->limit); 263 GNUNET_snprintf (offset_s, sizeof (offset_s), "%llu", 264 (unsigned long long) gpfh->offset); 265 gpfh->url = TALER_url_join (gpfh->base_url, 266 "private/fountains", 267 "limit", limit_s, 268 "offset", offset_s, 269 NULL); 270 if (NULL == gpfh->url) 271 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 272 eh = TALER_MERCHANT_curl_easy_get_ (gpfh->url); 273 if (NULL == eh) 274 { 275 GNUNET_break (0); 276 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 277 } 278 gpfh->job = GNUNET_CURL_job_add (gpfh->ctx, 279 eh, 280 &handle_get_fountains_finished, 281 gpfh); 282 if (NULL == gpfh->job) 283 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 284 return TALER_EC_NONE; 285 } 286 287 288 void 289 TALER_MERCHANT_get_private_fountains_cancel ( 290 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh) 291 { 292 if (NULL != gpfh->job) 293 { 294 GNUNET_CURL_job_cancel (gpfh->job); 295 gpfh->job = NULL; 296 } 297 GNUNET_free (gpfh->url); 298 GNUNET_free (gpfh->base_url); 299 GNUNET_free (gpfh); 300 } 301 302 303 /* end of merchant_api_get-private-fountains.c */