merchant_api_get-private-donau.c (8943B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024-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 Lesser General Public License as published by the Free Software 7 Foundation; either version 2.1, 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 Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License along with 14 TALER; see the file COPYING.LGPL. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file merchant_api_get-private-donau-new.c 19 * @brief Implementation of the GET /private/donau request 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include <curl/curl.h> 24 #include <jansson.h> 25 #include <microhttpd.h> /* just for HTTP status codes */ 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_curl_lib.h> 28 #include <taler/taler-merchant/get-private-donau.h> 29 #include "merchant_api_curl_defaults.h" 30 #include <taler/taler_json_lib.h> 31 32 33 /** 34 * Maximum number of Donau instances permitted. 35 */ 36 #define MAX_DONAU_INSTANCES 1024 37 38 39 /** 40 * Handle for a GET /private/donau operation. 41 */ 42 struct TALER_MERCHANT_GetPrivateDonauHandle 43 { 44 /** 45 * Base URL of the merchant backend. 46 */ 47 char *base_url; 48 49 /** 50 * The full URL for this request. 51 */ 52 char *url; 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_MERCHANT_GetPrivateDonauCallback cb; 63 64 /** 65 * Closure for @a cb. 66 */ 67 TALER_MERCHANT_GET_PRIVATE_DONAU_RESULT_CLOSURE *cb_cls; 68 69 /** 70 * Reference to the execution context. 71 */ 72 struct GNUNET_CURL_Context *ctx; 73 }; 74 75 76 /** 77 * Parse Donau instance information from @a ia. 78 * 79 * @param ia JSON array (or NULL!) with Donau instance data 80 * @param[in] dgr partially filled response 81 * @param gpdh operation handle 82 * @return #GNUNET_OK on success 83 */ 84 static enum GNUNET_GenericReturnValue 85 parse_donau_instances (const json_t *ia, 86 struct TALER_MERCHANT_GetPrivateDonauResponse *dgr, 87 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh) 88 { 89 unsigned int instances_len = (unsigned int) json_array_size (ia); 90 struct DONAU_Keys *donau_keys_ptr = NULL; 91 92 if ( (json_array_size (ia) != (size_t) instances_len) || 93 (instances_len > MAX_DONAU_INSTANCES) ) 94 { 95 GNUNET_break (0); 96 return GNUNET_SYSERR; 97 } 98 { 99 struct TALER_MERCHANT_GetPrivateDonauDonauInstanceEntry instances[ 100 GNUNET_NZL (instances_len)]; 101 size_t index; 102 json_t *value; 103 104 json_array_foreach (ia, index, value) { 105 struct TALER_MERCHANT_GetPrivateDonauDonauInstanceEntry *instance = 106 &instances[index]; 107 const json_t *donau_keys_json = NULL; 108 struct GNUNET_JSON_Specification spec[] = { 109 GNUNET_JSON_spec_uint64 ("donau_instance_serial", 110 &instance->donau_instance_serial), 111 GNUNET_JSON_spec_string ("donau_url", 112 &instance->donau_url), 113 GNUNET_JSON_spec_string ("charity_name", 114 &instance->charity_name), 115 GNUNET_JSON_spec_fixed_auto ("charity_pub_key", 116 &instance->charity_pub_key), 117 GNUNET_JSON_spec_uint64 ("charity_id", 118 &instance->charity_id), 119 TALER_JSON_spec_amount_any ("charity_max_per_year", 120 &instance->charity_max_per_year), 121 TALER_JSON_spec_amount_any ("charity_receipts_to_date", 122 &instance->charity_receipts_to_date), 123 GNUNET_JSON_spec_int64 ("current_year", 124 &instance->current_year), 125 GNUNET_JSON_spec_mark_optional ( 126 GNUNET_JSON_spec_object_const ("donau_keys_json", 127 &donau_keys_json), 128 NULL), 129 GNUNET_JSON_spec_end () 130 }; 131 132 if (GNUNET_OK != 133 GNUNET_JSON_parse (value, 134 spec, 135 NULL, NULL)) 136 { 137 GNUNET_break_op (0); 138 return GNUNET_SYSERR; 139 } 140 141 /* Parse the Donau keys */ 142 if (NULL != donau_keys_json) 143 { 144 donau_keys_ptr = DONAU_keys_from_json (donau_keys_json); 145 if (NULL == donau_keys_ptr) 146 { 147 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 148 "Failed to convert donau keys from JSON\n"); 149 return GNUNET_SYSERR; 150 } 151 instance->donau_keys = donau_keys_ptr; 152 } 153 } 154 dgr->details.ok.donau_instances_length = instances_len; 155 dgr->details.ok.donau_instances = instances; 156 gpdh->cb (gpdh->cb_cls, 157 dgr); 158 gpdh->cb = NULL; 159 if (NULL != donau_keys_ptr) 160 { 161 DONAU_keys_decref (donau_keys_ptr); 162 donau_keys_ptr = NULL; 163 } 164 } 165 return GNUNET_OK; 166 } 167 168 169 /** 170 * Function called when we're done processing the 171 * HTTP GET /private/donau request. 172 * 173 * @param cls the `struct TALER_MERCHANT_GetPrivateDonauHandle` 174 * @param response_code HTTP response code, 0 on error 175 * @param response response body, NULL if not in JSON 176 */ 177 static void 178 handle_get_donau_finished (void *cls, 179 long response_code, 180 const void *response) 181 { 182 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh = cls; 183 const json_t *json = response; 184 struct TALER_MERCHANT_GetPrivateDonauResponse dgr = { 185 .hr.http_status = (unsigned int) response_code, 186 .hr.reply = json 187 }; 188 189 gpdh->job = NULL; 190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 191 "Got /private/donau response with status code %u\n", 192 (unsigned int) response_code); 193 switch (response_code) 194 { 195 case MHD_HTTP_OK: 196 { 197 const json_t *donau_instances; 198 struct GNUNET_JSON_Specification spec[] = { 199 GNUNET_JSON_spec_array_const ("donau_instances", 200 &donau_instances), 201 GNUNET_JSON_spec_end () 202 }; 203 204 if (GNUNET_OK != 205 GNUNET_JSON_parse (json, 206 spec, 207 NULL, NULL)) 208 { 209 dgr.hr.http_status = 0; 210 dgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 211 break; 212 } 213 if (GNUNET_OK == 214 parse_donau_instances (donau_instances, 215 &dgr, 216 gpdh)) 217 { 218 TALER_MERCHANT_get_private_donau_cancel (gpdh); 219 return; 220 } 221 dgr.hr.http_status = 0; 222 dgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 223 break; 224 } 225 case MHD_HTTP_UNAUTHORIZED: 226 case MHD_HTTP_NOT_FOUND: 227 dgr.hr.ec = TALER_JSON_get_error_code (json); 228 dgr.hr.hint = TALER_JSON_get_error_hint (json); 229 break; 230 default: 231 dgr.hr.ec = TALER_JSON_get_error_code (json); 232 dgr.hr.hint = TALER_JSON_get_error_hint (json); 233 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 234 "Unexpected response code %u/%d\n", 235 (unsigned int) response_code, 236 (int) dgr.hr.ec); 237 break; 238 } 239 gpdh->cb (gpdh->cb_cls, 240 &dgr); 241 TALER_MERCHANT_get_private_donau_cancel (gpdh); 242 } 243 244 245 struct TALER_MERCHANT_GetPrivateDonauHandle * 246 TALER_MERCHANT_get_private_donau_create ( 247 struct GNUNET_CURL_Context *ctx, 248 const char *url) 249 { 250 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh; 251 252 gpdh = GNUNET_new (struct TALER_MERCHANT_GetPrivateDonauHandle); 253 gpdh->ctx = ctx; 254 gpdh->base_url = GNUNET_strdup (url); 255 return gpdh; 256 } 257 258 259 enum TALER_ErrorCode 260 TALER_MERCHANT_get_private_donau_start ( 261 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh, 262 TALER_MERCHANT_GetPrivateDonauCallback cb, 263 TALER_MERCHANT_GET_PRIVATE_DONAU_RESULT_CLOSURE *cb_cls) 264 { 265 CURL *eh; 266 267 gpdh->cb = cb; 268 gpdh->cb_cls = cb_cls; 269 gpdh->url = TALER_url_join (gpdh->base_url, 270 "private/donau", 271 NULL); 272 if (NULL == gpdh->url) 273 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 274 eh = TALER_MERCHANT_curl_easy_get_ (gpdh->url); 275 if (NULL == eh) 276 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 277 gpdh->job = GNUNET_CURL_job_add (gpdh->ctx, 278 eh, 279 &handle_get_donau_finished, 280 gpdh); 281 if (NULL == gpdh->job) 282 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 283 return TALER_EC_NONE; 284 } 285 286 287 void 288 TALER_MERCHANT_get_private_donau_cancel ( 289 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh) 290 { 291 if (NULL != gpdh->job) 292 { 293 GNUNET_CURL_job_cancel (gpdh->job); 294 gpdh->job = NULL; 295 } 296 GNUNET_free (gpdh->url); 297 GNUNET_free (gpdh->base_url); 298 GNUNET_free (gpdh); 299 } 300 301 302 /* end of merchant_api_get-private-donau-new.c */