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