donau_api_charities_get.c (8059B)
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 6 under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, see 17 <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file lib/donau_api_charities_get.c 22 * @brief Implementation of the "handle" component of the donau's HTTP API 23 * @author Lukas Matyja 24 */ 25 #include <gnunet/gnunet_curl_lib.h> 26 #include <taler/taler_json_lib.h> 27 #include "donau_service.h" 28 #include "donau_api_curl_defaults.h" 29 #include "donau_json_lib.h" 30 31 32 /** 33 * Handle for a GET /charities request. 34 */ 35 struct DONAU_CharitiesGetHandle 36 { 37 /** 38 * The url for the /charities request. 39 */ 40 char *url; 41 42 /** 43 * Entry for this request with the `struct GNUNET_CURL_Context`. 44 */ 45 struct GNUNET_CURL_Job *job; 46 47 /** 48 * Function to call with the result. 49 */ 50 DONAU_GetCharitiesResponseCallback cb; 51 52 /** 53 * Closure to pass to @e cb. 54 */ 55 void *cb_cls; 56 57 }; 58 59 /** 60 * Decode the JSON in @a resp_obj from the /charities response 61 * and store the data in the @a charities_data. 62 * 63 * @param[in] resp_obj JSON object to parse 64 * @param[in] cgh contains the callback function 65 * @param[out] gcresp where to store the results we decoded 66 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 67 * (malformed JSON) 68 */ 69 static enum GNUNET_GenericReturnValue 70 handle_charities_get_ok (const json_t *resp_obj, 71 struct DONAU_CharitiesGetHandle *cgh, 72 struct DONAU_GetCharitiesResponse *gcresp) 73 { 74 struct DONAU_CharitySummary *charities = NULL; 75 const json_t *ca = json_object_get (resp_obj, 76 "charities"); 77 size_t num_charity 78 = json_array_size (ca); 79 80 if (JSON_ARRAY != json_typeof (ca)) 81 { 82 GNUNET_break_op (0); 83 return GNUNET_SYSERR; 84 } 85 if (0 != num_charity) 86 { 87 size_t index; 88 json_t *charity_obj; 89 90 charities = GNUNET_new_array (num_charity, 91 struct DONAU_CharitySummary); 92 json_array_foreach (ca, 93 index, 94 charity_obj) 95 { 96 struct GNUNET_JSON_Specification spec[] = { 97 GNUNET_JSON_spec_uint64 ("charity_id", 98 &charities[index].charity_id), 99 GNUNET_JSON_spec_string ("charity_name", 100 &charities[index].name), 101 TALER_JSON_spec_amount_any ("max_per_year", 102 &charities[index].max_per_year), 103 TALER_JSON_spec_amount_any ("receipts_to_date", 104 &charities[index].receipts_to_date), 105 GNUNET_JSON_spec_end () 106 }; 107 108 if (GNUNET_OK != 109 GNUNET_JSON_parse (charity_obj, 110 spec, 111 NULL, 112 NULL)) 113 { 114 GNUNET_break_op (0); 115 return GNUNET_SYSERR; 116 } 117 } 118 gcresp->details.ok.num_charities = num_charity; 119 gcresp->details.ok.charities = charities; 120 } 121 cgh->cb (cgh->cb_cls, 122 gcresp); 123 cgh->cb = NULL; 124 GNUNET_free (charities); 125 return GNUNET_OK; 126 } 127 128 129 /** 130 * Callback used when downloading the reply to a /charities request 131 * is complete. 132 * 133 * @param cls the `struct KeysRequest` 134 * @param response_code HTTP response code, 0 on error 135 * @param resp_obj parsed JSON result, NULL on error 136 */ 137 static void 138 handle_charities_get_finished (void *cls, 139 long response_code, 140 const void *resp_obj) 141 { 142 struct DONAU_CharitiesGetHandle *cgh = cls; 143 const json_t *j = resp_obj; 144 struct DONAU_GetCharitiesResponse gcresp = { 145 .hr.reply = j, 146 .hr.http_status = (unsigned int) response_code 147 }; 148 149 cgh->job = NULL; 150 switch (response_code) 151 { 152 case 0: 153 gcresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 154 break; 155 case MHD_HTTP_OK: 156 if (GNUNET_OK != 157 handle_charities_get_ok (j, 158 cgh, 159 &gcresp)) 160 { 161 gcresp.hr.http_status = 0; 162 gcresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 163 } 164 break; 165 case MHD_HTTP_BAD_REQUEST: 166 /* This should never happen, either us or the donau is buggy 167 (or API version conflict); just pass JSON reply to the application */ 168 gcresp.hr.ec = TALER_JSON_get_error_code (j); 169 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 170 break; 171 case MHD_HTTP_FORBIDDEN: 172 /* Nothing really to verify */ 173 gcresp.hr.ec = TALER_JSON_get_error_code (j); 174 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 175 break; 176 case MHD_HTTP_NOT_FOUND: 177 /* Nothing really to verify, this should never 178 happen, we should pass the JSON reply to the application */ 179 gcresp.hr.ec = TALER_JSON_get_error_code (j); 180 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 181 break; 182 case MHD_HTTP_INTERNAL_SERVER_ERROR: 183 /* Server had an internal issue; we should retry, but this API 184 leaves this to the application */ 185 gcresp.hr.ec = TALER_JSON_get_error_code (j); 186 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 187 break; 188 default: 189 /* unexpected response code */ 190 GNUNET_break_op (0); 191 gcresp.hr.ec = TALER_JSON_get_error_code (j); 192 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 193 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 194 "Unexpected response code %u/%d for GET %s\n", 195 (unsigned int) response_code, 196 (int) gcresp.hr.ec, 197 cgh->url); 198 break; 199 } 200 201 if (NULL != cgh->cb) 202 { 203 cgh->cb (cgh->cb_cls, 204 &gcresp); 205 cgh->cb = NULL; 206 } 207 DONAU_charities_get_cancel (cgh); 208 } 209 210 211 struct DONAU_CharitiesGetHandle * 212 DONAU_charities_get ( 213 struct GNUNET_CURL_Context *ctx, 214 const char *url, 215 const struct DONAU_BearerToken *bearer, // FIXME-#9435: check authorization 216 DONAU_GetCharitiesResponseCallback cb, 217 void *cb_cls) 218 { 219 struct DONAU_CharitiesGetHandle *cgh; 220 CURL *eh; 221 const char *arg_str = "charities"; 222 223 TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", 224 url); 225 cgh = GNUNET_new (struct DONAU_CharitiesGetHandle); 226 cgh->url = GNUNET_strdup (url); 227 cgh->cb = cb; 228 cgh->cb_cls = cb_cls; 229 cgh->url = TALER_url_join (url, 230 arg_str, 231 NULL); 232 if (NULL == cgh->url) 233 { 234 GNUNET_free (cgh); 235 return NULL; 236 } 237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 238 "Requesting all charities with URL `%s'.\n", 239 cgh->url); 240 eh = DONAU_curl_easy_get_ (cgh->url); 241 if (NULL == eh) 242 { 243 GNUNET_break (0); 244 GNUNET_free (cgh->url); 245 GNUNET_free (cgh); 246 return NULL; 247 } 248 cgh->job = GNUNET_CURL_job_add_with_ct_json (ctx, 249 eh, 250 &handle_charities_get_finished, 251 cgh); 252 GNUNET_assert (NULL != cgh->job); 253 if (NULL != bearer) 254 { 255 struct curl_slist *auth; 256 char *hdr; 257 258 GNUNET_asprintf (&hdr, 259 "%s: Bearer %s", 260 MHD_HTTP_HEADER_AUTHORIZATION, 261 bearer->token); 262 auth = curl_slist_append (NULL, 263 hdr); 264 GNUNET_free (hdr); 265 GNUNET_CURL_extend_headers (cgh->job, 266 auth); 267 curl_slist_free_all (auth); 268 } 269 return cgh; 270 } 271 272 273 void 274 DONAU_charities_get_cancel ( 275 struct DONAU_CharitiesGetHandle *cgh) 276 { 277 if (NULL != cgh->job) 278 { 279 GNUNET_CURL_job_cancel (cgh->job); 280 cgh->job = NULL; 281 } 282 GNUNET_free (cgh->url); 283 GNUNET_free (cgh); 284 }