donau_api_charities_get.c (8229B)
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 79 if ( (NULL == ca) || 80 (! json_is_array (ca)) ) 81 { 82 GNUNET_break_op (0); 83 return GNUNET_SYSERR; 84 } 85 num_charity = json_array_size (ca); 86 if (0 != num_charity) 87 { 88 size_t index; 89 json_t *charity_obj; 90 91 charities = GNUNET_new_array (num_charity, 92 struct DONAU_CharitySummary); 93 json_array_foreach (ca, 94 index, 95 charity_obj) 96 { 97 struct GNUNET_JSON_Specification spec[] = { 98 GNUNET_JSON_spec_uint64 ("charity_id", 99 &charities[index].charity_id), 100 GNUNET_JSON_spec_string ("charity_name", 101 &charities[index].name), 102 TALER_JSON_spec_amount_any ("max_per_year", 103 &charities[index].max_per_year), 104 TALER_JSON_spec_amount_any ("receipts_to_date", 105 &charities[index].receipts_to_date), 106 GNUNET_JSON_spec_end () 107 }; 108 109 if (GNUNET_OK != 110 GNUNET_JSON_parse (charity_obj, 111 spec, 112 NULL, 113 NULL)) 114 { 115 GNUNET_break_op (0); 116 GNUNET_free (charities); 117 return GNUNET_SYSERR; 118 } 119 } 120 gcresp->details.ok.num_charities = num_charity; 121 gcresp->details.ok.charities = charities; 122 } 123 cgh->cb (cgh->cb_cls, 124 gcresp); 125 cgh->cb = NULL; 126 GNUNET_free (charities); 127 return GNUNET_OK; 128 } 129 130 131 /** 132 * Callback used when downloading the reply to a /charities request 133 * is complete. 134 * 135 * @param cls the `struct KeysRequest` 136 * @param response_code HTTP response code, 0 on error 137 * @param resp_obj parsed JSON result, NULL on error 138 */ 139 static void 140 handle_charities_get_finished (void *cls, 141 long response_code, 142 const void *resp_obj) 143 { 144 struct DONAU_CharitiesGetHandle *cgh = cls; 145 const json_t *j = resp_obj; 146 struct DONAU_GetCharitiesResponse gcresp = { 147 .hr.reply = j, 148 .hr.http_status = (unsigned int) response_code 149 }; 150 151 cgh->job = NULL; 152 switch (response_code) 153 { 154 case 0: 155 gcresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 156 break; 157 case MHD_HTTP_OK: 158 if (GNUNET_OK != 159 handle_charities_get_ok (j, 160 cgh, 161 &gcresp)) 162 { 163 gcresp.hr.http_status = 0; 164 gcresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 165 } 166 break; 167 case MHD_HTTP_NO_CONTENT: 168 /* No charities are registered yet; a normal, non-error outcome 169 (empty list, empty body). */ 170 break; 171 case MHD_HTTP_BAD_REQUEST: 172 /* This should never happen, either us or the donau is buggy 173 (or API version conflict); just pass JSON reply to the application */ 174 gcresp.hr.ec = TALER_JSON_get_error_code (j); 175 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 176 break; 177 case MHD_HTTP_FORBIDDEN: 178 /* Nothing really to verify */ 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_NOT_FOUND: 183 /* Nothing really to verify, this should never 184 happen, we should pass the JSON reply 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 case MHD_HTTP_INTERNAL_SERVER_ERROR: 189 /* Server had an internal issue; we should retry, but this API 190 leaves this to the application */ 191 gcresp.hr.ec = TALER_JSON_get_error_code (j); 192 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 193 break; 194 default: 195 /* unexpected response code */ 196 GNUNET_break_op (0); 197 gcresp.hr.ec = TALER_JSON_get_error_code (j); 198 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 199 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 200 "Unexpected response code %u/%d for GET %s\n", 201 (unsigned int) response_code, 202 (int) gcresp.hr.ec, 203 cgh->url); 204 break; 205 } 206 207 if (NULL != cgh->cb) 208 { 209 cgh->cb (cgh->cb_cls, 210 &gcresp); 211 cgh->cb = NULL; 212 } 213 DONAU_charities_get_cancel (cgh); 214 } 215 216 217 struct DONAU_CharitiesGetHandle * 218 DONAU_charities_get ( 219 struct GNUNET_CURL_Context *ctx, 220 const char *url, 221 const struct DONAU_BearerToken *bearer, // FIXME-#9435: check authorization 222 DONAU_GetCharitiesResponseCallback cb, 223 void *cb_cls) 224 { 225 struct DONAU_CharitiesGetHandle *cgh; 226 CURL *eh; 227 const char *arg_str = "charities"; 228 229 TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", 230 url); 231 cgh = GNUNET_new (struct DONAU_CharitiesGetHandle); 232 cgh->cb = cb; 233 cgh->cb_cls = cb_cls; 234 cgh->url = TALER_url_join (url, 235 arg_str, 236 NULL); 237 if (NULL == cgh->url) 238 { 239 GNUNET_free (cgh); 240 return NULL; 241 } 242 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 243 "Requesting all charities with URL `%s'.\n", 244 cgh->url); 245 eh = DONAU_curl_easy_get_ (cgh->url); 246 if (NULL == eh) 247 { 248 GNUNET_break (0); 249 GNUNET_free (cgh->url); 250 GNUNET_free (cgh); 251 return NULL; 252 } 253 cgh->job = GNUNET_CURL_job_add_with_ct_json (ctx, 254 eh, 255 &handle_charities_get_finished, 256 cgh); 257 GNUNET_assert (NULL != cgh->job); 258 if (NULL != bearer) 259 { 260 struct curl_slist *auth; 261 char *hdr; 262 263 GNUNET_asprintf (&hdr, 264 "%s: Bearer %s", 265 MHD_HTTP_HEADER_AUTHORIZATION, 266 bearer->token); 267 auth = curl_slist_append (NULL, 268 hdr); 269 GNUNET_free (hdr); 270 GNUNET_CURL_extend_headers (cgh->job, 271 auth); 272 curl_slist_free_all (auth); 273 } 274 return cgh; 275 } 276 277 278 void 279 DONAU_charities_get_cancel ( 280 struct DONAU_CharitiesGetHandle *cgh) 281 { 282 if (NULL != cgh->job) 283 { 284 GNUNET_CURL_job_cancel (cgh->job); 285 cgh->job = NULL; 286 } 287 GNUNET_free (cgh->url); 288 GNUNET_free (cgh); 289 }