merchant_api_get-private-fountains.c (7921B)
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 /** 48 * The full URL for this request. 49 */ 50 char *url; 51 52 /** 53 * Handle for the request. 54 */ 55 struct GNUNET_CURL_Job *job; 56 57 /** 58 * Function to call with the result. 59 */ 60 TALER_MERCHANT_GetPrivateFountainsCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_GET_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls; 66 67 /** 68 * Reference to the execution context. 69 */ 70 struct GNUNET_CURL_Context *ctx; 71 }; 72 73 74 /** 75 * Parse the "fountains" array and invoke the callback. 76 * 77 * @param gpfh operation handle 78 * @param[in,out] gfr response to complete and pass to the callback 79 */ 80 static void 81 handle_ok (struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh, 82 struct TALER_MERCHANT_GetPrivateFountainsResponse *gfr) 83 { 84 const json_t *jfountains; 85 struct GNUNET_JSON_Specification spec[] = { 86 GNUNET_JSON_spec_array_const ("fountains", 87 &jfountains), 88 GNUNET_JSON_spec_end () 89 }; 90 91 if (GNUNET_OK != 92 GNUNET_JSON_parse (gfr->hr.reply, 93 spec, 94 NULL, 95 NULL)) 96 { 97 GNUNET_break_op (0); 98 gfr->hr.http_status = 0; 99 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 100 gpfh->cb (gpfh->cb_cls, 101 gfr); 102 return; 103 } 104 /* This endpoint is not paginated, so bound allocation bytes rather than 105 imposing the much smaller limit used for grants within one fountain. 106 Check before narrowing the count or multiplying it by the entry size. */ 107 if (json_array_size (jfountains) >= 108 GNUNET_MAX_MALLOC_CHECKED / sizeof (struct TALER_MERCHANT_FountainEntry)) 109 { 110 gfr->hr.http_status = 0; 111 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 112 gpfh->cb (gpfh->cb_cls, 113 gfr); 114 return; 115 } 116 { 117 unsigned int len = (unsigned int) json_array_size (jfountains); 118 struct TALER_MERCHANT_FountainEntry *fountains 119 = GNUNET_new_array (GNUNET_NZL (len), 120 struct TALER_MERCHANT_FountainEntry); 121 size_t idx; 122 json_t *jf; 123 124 json_array_foreach ((json_t *) jfountains, idx, jf) 125 { 126 struct TALER_MERCHANT_FountainEntry *fe = &fountains[idx]; 127 struct GNUNET_JSON_Specification ispec[] = { 128 GNUNET_JSON_spec_string ("fountain_id", 129 &fe->fountain_id), 130 GNUNET_JSON_spec_string ("description", 131 &fe->description), 132 GNUNET_JSON_spec_end () 133 }; 134 135 if (GNUNET_OK != 136 GNUNET_JSON_parse (jf, 137 ispec, 138 NULL, 139 NULL)) 140 { 141 GNUNET_break_op (0); 142 gfr->hr.http_status = 0; 143 gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 144 GNUNET_free (fountains); 145 gpfh->cb (gpfh->cb_cls, 146 gfr); 147 return; 148 } 149 } 150 gfr->details.ok.fountains = fountains; 151 gfr->details.ok.fountains_len = len; 152 gpfh->cb (gpfh->cb_cls, 153 gfr); 154 GNUNET_free (fountains); 155 } 156 } 157 158 159 /** 160 * Function called when we're done processing the 161 * HTTP GET /private/fountains request. 162 * 163 * @param cls the `struct TALER_MERCHANT_GetPrivateFountainsHandle` 164 * @param response_code HTTP response code, 0 on error 165 * @param response response body, NULL if not in JSON 166 */ 167 static void 168 handle_get_fountains_finished (void *cls, 169 long response_code, 170 const void *response) 171 { 172 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh = cls; 173 const json_t *json = response; 174 struct TALER_MERCHANT_GetPrivateFountainsResponse gfr = { 175 .hr.http_status = (unsigned int) response_code, 176 .hr.reply = json 177 }; 178 179 gpfh->job = NULL; 180 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 181 "GET /private/fountains completed with response code %u\n", 182 (unsigned int) response_code); 183 switch (response_code) 184 { 185 case 0: 186 gfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 187 break; 188 case MHD_HTTP_OK: 189 handle_ok (gpfh, 190 &gfr); 191 TALER_MERCHANT_get_private_fountains_cancel (gpfh); 192 return; 193 case MHD_HTTP_UNAUTHORIZED: 194 case MHD_HTTP_FORBIDDEN: 195 case MHD_HTTP_NOT_FOUND: 196 case MHD_HTTP_INTERNAL_SERVER_ERROR: 197 gfr.hr.ec = TALER_JSON_get_error_code (json); 198 gfr.hr.hint = TALER_JSON_get_error_hint (json); 199 break; 200 default: 201 TALER_MERCHANT_parse_error_details_ (json, 202 response_code, 203 &gfr.hr); 204 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 205 "Unexpected response code %u/%d\n", 206 (unsigned int) response_code, 207 (int) gfr.hr.ec); 208 GNUNET_break_op (0); 209 break; 210 } 211 gpfh->cb (gpfh->cb_cls, 212 &gfr); 213 TALER_MERCHANT_get_private_fountains_cancel (gpfh); 214 } 215 216 217 struct TALER_MERCHANT_GetPrivateFountainsHandle * 218 TALER_MERCHANT_get_private_fountains_create ( 219 struct GNUNET_CURL_Context *ctx, 220 const char *url) 221 { 222 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh; 223 224 gpfh = GNUNET_new (struct TALER_MERCHANT_GetPrivateFountainsHandle); 225 gpfh->ctx = ctx; 226 gpfh->base_url = GNUNET_strdup (url); 227 return gpfh; 228 } 229 230 231 enum TALER_ErrorCode 232 TALER_MERCHANT_get_private_fountains_start ( 233 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh, 234 TALER_MERCHANT_GetPrivateFountainsCallback cb, 235 TALER_MERCHANT_GET_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls) 236 { 237 CURL *eh; 238 239 gpfh->cb = cb; 240 gpfh->cb_cls = cb_cls; 241 gpfh->url = TALER_url_join (gpfh->base_url, 242 "private/fountains", 243 NULL); 244 if (NULL == gpfh->url) 245 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 246 eh = TALER_MERCHANT_curl_easy_get_ (gpfh->url); 247 if (NULL == eh) 248 { 249 GNUNET_break (0); 250 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 251 } 252 gpfh->job = GNUNET_CURL_job_add (gpfh->ctx, 253 eh, 254 &handle_get_fountains_finished, 255 gpfh); 256 if (NULL == gpfh->job) 257 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 258 return TALER_EC_NONE; 259 } 260 261 262 void 263 TALER_MERCHANT_get_private_fountains_cancel ( 264 struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh) 265 { 266 if (NULL != gpfh->job) 267 { 268 GNUNET_CURL_job_cancel (gpfh->job); 269 gpfh->job = NULL; 270 } 271 GNUNET_free (gpfh->url); 272 GNUNET_free (gpfh->base_url); 273 GNUNET_free (gpfh); 274 } 275 276 277 /* end of merchant_api_get-private-fountains.c */