merchant_api_get_donau_instance.c (8797B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024 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 <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file merchant_api_get_donau_instance.c 18 * @brief Implementation of the GET /donau request of the merchant's HTTP API 19 * @author Bohdan Potuzhnyi 20 * @author Vlada Svirsh 21 */ 22 23 #include "platform.h" 24 #include <curl/curl.h> 25 #include <jansson.h> 26 #include <microhttpd.h> /* for HTTP status codes */ 27 #include <gnunet/gnunet_util_lib.h> 28 #include <gnunet/gnunet_curl_lib.h> 29 #include "taler_merchant_service.h" 30 #include "merchant_api_curl_defaults.h" 31 #include <taler/taler_json_lib.h> 32 #include <taler/taler_signatures.h> 33 /* DONAU RELATED IMPORTS */ 34 #include "taler_merchant_donau.h" 35 #include <donau/donau_service.h> 36 37 /** 38 * Handle for a GET /donau operation. 39 */ 40 struct TALER_MERCHANT_DonauInstanceGetHandle 41 { 42 /** 43 * The URL for this request. 44 */ 45 char *url; 46 47 /** 48 * Handle for the request. 49 */ 50 struct GNUNET_CURL_Job *job; 51 52 /** 53 * Function to call with the result. 54 */ 55 TALER_MERCHANT_DonauInstanceGetCallback cb; 56 57 /** 58 * Closure for @a cb. 59 */ 60 void *cb_cls; 61 62 /** 63 * Reference to the execution context. 64 */ 65 struct GNUNET_CURL_Context *ctx; 66 }; 67 68 /** 69 * Parse Donau instance information from @a ia. 70 * 71 * @param ia JSON array (or NULL!) with Donau instance data 72 * @param igr response to fill 73 * @param dgh operation handle 74 * @return #GNUNET_OK on success 75 */ 76 static enum GNUNET_GenericReturnValue 77 parse_donau_instances (const json_t *ia, 78 struct TALER_MERCHANT_DonauInstanceGetResponse *igr, 79 struct TALER_MERCHANT_DonauInstanceGetHandle *dgh) 80 { 81 unsigned int instances_len = (unsigned int) json_array_size (ia); 82 struct TALER_MERCHANT_DonauInstanceEntry 83 instances[GNUNET_NZL (instances_len)]; 84 size_t index; 85 json_t *value; 86 struct DONAU_Keys *donau_keys_ptr = NULL; 87 88 if ((json_array_size (ia) != (size_t) instances_len)) 89 { 90 GNUNET_break (0); 91 return GNUNET_SYSERR; 92 } 93 94 json_array_foreach (ia, 95 index, 96 value) 97 { 98 struct TALER_MERCHANT_DonauInstanceEntry *instance = &instances[index]; 99 const json_t *donau_keys_json = NULL; 100 struct GNUNET_JSON_Specification spec[] = { 101 GNUNET_JSON_spec_uint64 ("donau_instance_serial", 102 &instance->donau_instance_serial), 103 GNUNET_JSON_spec_string ("donau_url", 104 &instance->donau_url), 105 GNUNET_JSON_spec_string ("charity_name", 106 &instance->charity_name), 107 GNUNET_JSON_spec_fixed_auto ("charity_pub_key", 108 &instance->charity_pub_key), 109 GNUNET_JSON_spec_uint64 ("charity_id", 110 &instance->charity_id), 111 TALER_JSON_spec_amount_any ("charity_max_per_year", 112 &instance->charity_max_per_year), 113 TALER_JSON_spec_amount_any ("charity_receipts_to_date", 114 &instance->charity_receipts_to_date), 115 GNUNET_JSON_spec_int64 ("current_year", 116 &instance->current_year), 117 GNUNET_JSON_spec_mark_optional ( 118 GNUNET_JSON_spec_object_const ("donau_keys_json", 119 &donau_keys_json), 120 NULL), 121 GNUNET_JSON_spec_end () 122 }; 123 124 if (GNUNET_OK != 125 GNUNET_JSON_parse (value, 126 spec, 127 NULL, 128 NULL)) 129 { 130 GNUNET_break_op (0); 131 return GNUNET_SYSERR; 132 } 133 134 /* Parse the Donau keys */ 135 if (NULL != donau_keys_json) 136 { 137 donau_keys_ptr = DONAU_keys_from_json (donau_keys_json); 138 if (NULL == donau_keys_ptr) 139 { 140 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 141 "Failed to convert donau keys from JSON\n"); 142 return GNUNET_SYSERR; 143 } 144 instance->donau_keys = donau_keys_ptr; 145 } 146 } 147 148 igr->details.ok.donau_instances_length = instances_len; 149 igr->details.ok.donau_instances = instances; 150 dgh->cb (dgh->cb_cls, 151 igr); 152 dgh->cb = NULL; 153 if (NULL != donau_keys_ptr) 154 { 155 DONAU_keys_decref (donau_keys_ptr); 156 donau_keys_ptr= NULL; 157 } 158 return GNUNET_OK; 159 } 160 161 162 /** 163 * Function called when we're done processing the 164 * HTTP /donau request. 165 * 166 * @param cls the `struct TALER_MERCHANT_DonauInstanceGetHandle` 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_donau_instances_finished (void *cls, 172 long response_code, 173 const void *response) 174 { 175 struct TALER_MERCHANT_DonauInstanceGetHandle *dgh = cls; 176 const json_t *json = response; 177 struct TALER_MERCHANT_DonauInstanceGetResponse igr = { 178 .hr.http_status = (unsigned int) response_code, 179 .hr.reply = json 180 }; 181 182 dgh->job = NULL; 183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 184 "Got /donau response with status code %u\n", 185 (unsigned int) response_code); 186 187 switch (response_code) 188 { 189 case MHD_HTTP_OK: 190 { 191 const json_t *donau_instances; 192 struct GNUNET_JSON_Specification spec[] = { 193 GNUNET_JSON_spec_array_const ("donau_instances", 194 &donau_instances), 195 GNUNET_JSON_spec_end () 196 }; 197 198 if (GNUNET_OK != 199 GNUNET_JSON_parse (json, 200 spec, 201 NULL, 202 NULL)) 203 { 204 igr.hr.http_status = 0; 205 igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 206 break; 207 } 208 209 if (GNUNET_OK == 210 parse_donau_instances (donau_instances, 211 &igr, 212 dgh)) 213 { 214 TALER_MERCHANT_donau_instances_get_cancel (dgh); 215 return; 216 } 217 218 igr.hr.http_status = 0; 219 igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 220 break; 221 } 222 223 case MHD_HTTP_UNAUTHORIZED: 224 case MHD_HTTP_NOT_FOUND: 225 default: 226 igr.hr.ec = TALER_JSON_get_error_code (json); 227 igr.hr.hint = TALER_JSON_get_error_hint (json); 228 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 229 "Unexpected response code %u/%d\n", 230 (unsigned int) response_code, 231 (int) igr.hr.ec); 232 break; 233 } 234 235 dgh->cb (dgh->cb_cls, 236 &igr); 237 TALER_MERCHANT_donau_instances_get_cancel (dgh); 238 } 239 240 241 /** 242 * Initiate the GET /donau request. 243 * 244 * @param ctx CURL context 245 * @param backend_url base URL for the backend 246 * @param cb callback function to handle the response 247 * @param cb_cls closure for the callback function 248 * @return the handle for the operation, or NULL on error 249 */ 250 struct TALER_MERCHANT_DonauInstanceGetHandle * 251 TALER_MERCHANT_donau_instances_get (struct GNUNET_CURL_Context *ctx, 252 const char *backend_url, 253 TALER_MERCHANT_DonauInstanceGetCallback cb, 254 void *cb_cls) 255 { 256 struct TALER_MERCHANT_DonauInstanceGetHandle *dgh; 257 CURL *eh; 258 259 dgh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceGetHandle); 260 dgh->ctx = ctx; 261 dgh->cb = cb; 262 dgh->cb_cls = cb_cls; 263 dgh->url = TALER_url_join (backend_url, 264 "private/donau", 265 NULL); 266 if (NULL == dgh->url) 267 { 268 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 269 "Could not construct request URL.\n"); 270 GNUNET_free (dgh); 271 return NULL; 272 } 273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 274 "Requesting URL '%s'\n", 275 dgh->url); 276 eh = TALER_MERCHANT_curl_easy_get_ (dgh->url); 277 dgh->job = GNUNET_CURL_job_add (ctx, 278 eh, 279 &handle_get_donau_instances_finished, 280 dgh); 281 282 return dgh; 283 } 284 285 286 /** 287 * Cancel the GET /donau instances operation. 288 * 289 * @param dgh request to cancel. 290 */ 291 void 292 TALER_MERCHANT_donau_instances_get_cancel ( 293 struct TALER_MERCHANT_DonauInstanceGetHandle *dgh) 294 { 295 if (NULL != dgh->job) 296 GNUNET_CURL_job_cancel (dgh->job); 297 GNUNET_free (dgh->url); 298 GNUNET_free (dgh); 299 }