donau_api_charities_get.c (8086B)
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_BAD_REQUEST: 168 /* This should never happen, either us or the donau is buggy 169 (or API version conflict); just pass JSON reply to the application */ 170 gcresp.hr.ec = TALER_JSON_get_error_code (j); 171 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 172 break; 173 case MHD_HTTP_FORBIDDEN: 174 /* Nothing really to verify */ 175 gcresp.hr.ec = TALER_JSON_get_error_code (j); 176 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 177 break; 178 case MHD_HTTP_NOT_FOUND: 179 /* Nothing really to verify, this should never 180 happen, we should pass the JSON reply to the application */ 181 gcresp.hr.ec = TALER_JSON_get_error_code (j); 182 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 183 break; 184 case MHD_HTTP_INTERNAL_SERVER_ERROR: 185 /* Server had an internal issue; we should retry, but this API 186 leaves this to the application */ 187 gcresp.hr.ec = TALER_JSON_get_error_code (j); 188 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 189 break; 190 default: 191 /* unexpected response code */ 192 GNUNET_break_op (0); 193 gcresp.hr.ec = TALER_JSON_get_error_code (j); 194 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 195 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 196 "Unexpected response code %u/%d for GET %s\n", 197 (unsigned int) response_code, 198 (int) gcresp.hr.ec, 199 cgh->url); 200 break; 201 } 202 203 if (NULL != cgh->cb) 204 { 205 cgh->cb (cgh->cb_cls, 206 &gcresp); 207 cgh->cb = NULL; 208 } 209 DONAU_charities_get_cancel (cgh); 210 } 211 212 213 struct DONAU_CharitiesGetHandle * 214 DONAU_charities_get ( 215 struct GNUNET_CURL_Context *ctx, 216 const char *url, 217 const struct DONAU_BearerToken *bearer, // FIXME-#9435: check authorization 218 DONAU_GetCharitiesResponseCallback cb, 219 void *cb_cls) 220 { 221 struct DONAU_CharitiesGetHandle *cgh; 222 CURL *eh; 223 const char *arg_str = "charities"; 224 225 TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", 226 url); 227 cgh = GNUNET_new (struct DONAU_CharitiesGetHandle); 228 cgh->cb = cb; 229 cgh->cb_cls = cb_cls; 230 cgh->url = TALER_url_join (url, 231 arg_str, 232 NULL); 233 if (NULL == cgh->url) 234 { 235 GNUNET_free (cgh); 236 return NULL; 237 } 238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 239 "Requesting all charities with URL `%s'.\n", 240 cgh->url); 241 eh = DONAU_curl_easy_get_ (cgh->url); 242 if (NULL == eh) 243 { 244 GNUNET_break (0); 245 GNUNET_free (cgh->url); 246 GNUNET_free (cgh); 247 return NULL; 248 } 249 cgh->job = GNUNET_CURL_job_add_with_ct_json (ctx, 250 eh, 251 &handle_charities_get_finished, 252 cgh); 253 GNUNET_assert (NULL != cgh->job); 254 if (NULL != bearer) 255 { 256 struct curl_slist *auth; 257 char *hdr; 258 259 GNUNET_asprintf (&hdr, 260 "%s: Bearer %s", 261 MHD_HTTP_HEADER_AUTHORIZATION, 262 bearer->token); 263 auth = curl_slist_append (NULL, 264 hdr); 265 GNUNET_free (hdr); 266 GNUNET_CURL_extend_headers (cgh->job, 267 auth); 268 curl_slist_free_all (auth); 269 } 270 return cgh; 271 } 272 273 274 void 275 DONAU_charities_get_cancel ( 276 struct DONAU_CharitiesGetHandle *cgh) 277 { 278 if (NULL != cgh->job) 279 { 280 GNUNET_CURL_job_cancel (cgh->job); 281 cgh->job = NULL; 282 } 283 GNUNET_free (cgh->url); 284 GNUNET_free (cgh); 285 }