merchant_api_get-private-donau.c (9025B)
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 src/lib/merchant_api_get-private-donau.c 19 * @brief Implementation of the GET /private/donau request 20 * @author Christian Grothoff 21 */ 22 #include "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/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 ( 86 const json_t *ia, 87 struct TALER_MERCHANT_GetPrivateDonauResponse *dgr, 88 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh) 89 { 90 unsigned int instances_len = (unsigned int) json_array_size (ia); 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 enum GNUNET_GenericReturnValue ret = GNUNET_OK; 102 size_t index; 103 json_t *value; 104 105 memset (instances, 106 0, 107 sizeof (instances)); 108 json_array_foreach (ia, index, value) { 109 struct TALER_MERCHANT_GetPrivateDonauDonauInstanceEntry *instance = 110 &instances[index]; 111 const json_t *donau_keys_json = NULL; 112 struct GNUNET_JSON_Specification spec[] = { 113 GNUNET_JSON_spec_uint64 ("donau_instance_serial", 114 &instance->donau_instance_serial), 115 GNUNET_JSON_spec_string ("donau_url", 116 &instance->donau_url), 117 GNUNET_JSON_spec_string ("charity_name", 118 &instance->charity_name), 119 GNUNET_JSON_spec_fixed_auto ("charity_pub_key", 120 &instance->charity_pub_key), 121 GNUNET_JSON_spec_uint64 ("charity_id", 122 &instance->charity_id), 123 TALER_JSON_spec_amount_any ("charity_max_per_year", 124 &instance->charity_max_per_year), 125 TALER_JSON_spec_amount_any ("charity_receipts_to_date", 126 &instance->charity_receipts_to_date), 127 GNUNET_JSON_spec_int64 ("current_year", 128 &instance->current_year), 129 GNUNET_JSON_spec_mark_optional ( 130 GNUNET_JSON_spec_object_const ("donau_keys_json", 131 &donau_keys_json), 132 NULL), 133 GNUNET_JSON_spec_end () 134 }; 135 136 if (GNUNET_OK != 137 GNUNET_JSON_parse (value, 138 spec, 139 NULL, NULL)) 140 { 141 GNUNET_break_op (0); 142 ret = GNUNET_SYSERR; 143 break; 144 } 145 146 /* Parse the Donau keys */ 147 if (NULL != donau_keys_json) 148 { 149 instance->donau_keys = DONAU_keys_from_json (donau_keys_json); 150 if (NULL == instance->donau_keys) 151 { 152 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 153 "Failed to convert donau keys from JSON\n"); 154 ret = GNUNET_SYSERR; 155 break; 156 } 157 } 158 } 159 if (GNUNET_OK == ret) 160 { 161 dgr->details.ok.donau_instances_length = instances_len; 162 dgr->details.ok.donau_instances = instances; 163 gpdh->cb (gpdh->cb_cls, 164 dgr); 165 gpdh->cb = NULL; 166 } 167 for (unsigned int i = 0; i < instances_len; i++) 168 DONAU_keys_decref ((struct DONAU_Keys *) instances[i].donau_keys); 169 return ret; 170 } 171 } 172 173 174 /** 175 * Function called when we're done processing the 176 * HTTP GET /private/donau request. 177 * 178 * @param cls the `struct TALER_MERCHANT_GetPrivateDonauHandle` 179 * @param response_code HTTP response code, 0 on error 180 * @param response response body, NULL if not in JSON 181 */ 182 static void 183 handle_get_donau_finished (void *cls, 184 long response_code, 185 const void *response) 186 { 187 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh = cls; 188 const json_t *json = response; 189 struct TALER_MERCHANT_GetPrivateDonauResponse dgr = { 190 .hr.http_status = (unsigned int) response_code, 191 .hr.reply = json 192 }; 193 194 gpdh->job = NULL; 195 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 196 "Got /private/donau response with status code %u\n", 197 (unsigned int) response_code); 198 switch (response_code) 199 { 200 case MHD_HTTP_OK: 201 { 202 const json_t *donau_instances; 203 struct GNUNET_JSON_Specification spec[] = { 204 GNUNET_JSON_spec_array_const ("donau_instances", 205 &donau_instances), 206 GNUNET_JSON_spec_end () 207 }; 208 209 if (GNUNET_OK != 210 GNUNET_JSON_parse (json, 211 spec, 212 NULL, NULL)) 213 { 214 dgr.hr.http_status = 0; 215 dgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 216 break; 217 } 218 if (GNUNET_OK == 219 parse_donau_instances (donau_instances, 220 &dgr, 221 gpdh)) 222 { 223 TALER_MERCHANT_get_private_donau_cancel (gpdh); 224 return; 225 } 226 dgr.hr.http_status = 0; 227 dgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 228 break; 229 } 230 case MHD_HTTP_UNAUTHORIZED: 231 case MHD_HTTP_NOT_FOUND: 232 dgr.hr.ec = TALER_JSON_get_error_code (json); 233 dgr.hr.hint = TALER_JSON_get_error_hint (json); 234 break; 235 default: 236 dgr.hr.ec = TALER_JSON_get_error_code (json); 237 dgr.hr.hint = TALER_JSON_get_error_hint (json); 238 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 239 "Unexpected response code %u/%d\n", 240 (unsigned int) response_code, 241 (int) dgr.hr.ec); 242 break; 243 } 244 gpdh->cb (gpdh->cb_cls, 245 &dgr); 246 TALER_MERCHANT_get_private_donau_cancel (gpdh); 247 } 248 249 250 struct TALER_MERCHANT_GetPrivateDonauHandle * 251 TALER_MERCHANT_get_private_donau_create ( 252 struct GNUNET_CURL_Context *ctx, 253 const char *url) 254 { 255 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh; 256 257 gpdh = GNUNET_new (struct TALER_MERCHANT_GetPrivateDonauHandle); 258 gpdh->ctx = ctx; 259 gpdh->base_url = GNUNET_strdup (url); 260 return gpdh; 261 } 262 263 264 enum TALER_ErrorCode 265 TALER_MERCHANT_get_private_donau_start ( 266 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh, 267 TALER_MERCHANT_GetPrivateDonauCallback cb, 268 TALER_MERCHANT_GET_PRIVATE_DONAU_RESULT_CLOSURE *cb_cls) 269 { 270 CURL *eh; 271 272 gpdh->cb = cb; 273 gpdh->cb_cls = cb_cls; 274 gpdh->url = TALER_url_join (gpdh->base_url, 275 "private/donau", 276 NULL); 277 if (NULL == gpdh->url) 278 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 279 eh = TALER_MERCHANT_curl_easy_get_ (gpdh->url); 280 if (NULL == eh) 281 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 282 gpdh->job = GNUNET_CURL_job_add (gpdh->ctx, 283 eh, 284 &handle_get_donau_finished, 285 gpdh); 286 if (NULL == gpdh->job) 287 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 288 return TALER_EC_NONE; 289 } 290 291 292 void 293 TALER_MERCHANT_get_private_donau_cancel ( 294 struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh) 295 { 296 if (NULL != gpdh->job) 297 { 298 GNUNET_CURL_job_cancel (gpdh->job); 299 gpdh->job = NULL; 300 } 301 GNUNET_free (gpdh->url); 302 GNUNET_free (gpdh->base_url); 303 GNUNET_free (gpdh); 304 } 305 306 307 /* end of merchant_api_get-private-donau-new.c */