donau_api_charity_get.c (7908B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024, 2025 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_charity_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/$CHARITY_ID request. 34 */ 35 struct DONAU_CharityGetHandle 36 { 37 /** 38 * The url for the /charities/$CHARITY_ID 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_GetCharityResponseCallback cb; 51 52 /** 53 * Charity id we are querying. 54 */ 55 unsigned long long charity_id; 56 57 /** 58 * Closure to pass to @e cb. 59 */ 60 void *cb_cls; 61 62 }; 63 64 /** 65 * Decode the JSON in @a resp_obj from the /charities/$ID response 66 * and store the data in the @a charity_data. 67 * 68 * @param[in] resp_obj JSON object to parse 69 * @param[out] charity_data where to store the results we decoded 70 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 71 * (malformed JSON) 72 */ 73 static enum GNUNET_GenericReturnValue 74 handle_charity_get_ok (const json_t *resp_obj, 75 struct DONAU_CharityGetHandle *cgh, 76 struct DONAU_GetCharityResponse *gcresp) 77 { 78 struct DONAU_Charity *charity = &gcresp->details.ok.charity; 79 struct GNUNET_JSON_Specification spec[] = { 80 GNUNET_JSON_spec_fixed_auto ("charity_pub", 81 &charity->charity_pub), 82 GNUNET_JSON_spec_string ("name", 83 &charity->name), 84 GNUNET_JSON_spec_string ("url", 85 &charity->charity_url), 86 TALER_JSON_spec_amount_any ("max_per_year", 87 &charity->max_per_year), 88 TALER_JSON_spec_amount_any ("receipts_to_date", 89 &charity->receipts_to_date), 90 GNUNET_JSON_spec_uint64 ("current_year", 91 &charity->current_year), 92 GNUNET_JSON_spec_end () 93 }; 94 95 if (GNUNET_OK != 96 GNUNET_JSON_parse (resp_obj, 97 spec, 98 NULL, 99 NULL)) 100 { 101 GNUNET_break_op (0); 102 return GNUNET_SYSERR; 103 } 104 cgh->cb (cgh->cb_cls, 105 gcresp); 106 cgh->cb = NULL; 107 return GNUNET_OK; 108 } 109 110 111 /** 112 * Callback used when downloading the reply to a /charity request 113 * is complete. 114 * 115 * @param cls the `struct KeysRequest` 116 * @param response_code HTTP response code, 0 on error 117 * @param resp_obj parsed JSON result, NULL on error 118 */ 119 static void 120 handle_charity_get_finished (void *cls, 121 long response_code, 122 const void *resp_obj) 123 { 124 struct DONAU_CharityGetHandle *cgh = cls; 125 const json_t *j = resp_obj; 126 struct DONAU_GetCharityResponse gcresp = { 127 .hr.reply = j, 128 .hr.http_status = (unsigned int) response_code 129 }; 130 131 cgh->job = NULL; 132 switch (response_code) 133 { 134 case 0: 135 gcresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 136 break; 137 case MHD_HTTP_OK: 138 if (GNUNET_OK != 139 handle_charity_get_ok (j, 140 cgh, 141 &gcresp)) 142 { 143 gcresp.hr.http_status = 0; 144 gcresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 145 } 146 break; 147 case MHD_HTTP_BAD_REQUEST: 148 /* This should never happen, either us or the donau is buggy 149 (or API version conflict); just pass JSON reply to the application */ 150 gcresp.hr.ec = TALER_JSON_get_error_code (j); 151 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 152 break; 153 case MHD_HTTP_FORBIDDEN: 154 /* Nothing really to verify */ 155 gcresp.hr.ec = TALER_JSON_get_error_code (j); 156 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 157 break; 158 case MHD_HTTP_NOT_FOUND: 159 /* Nothing really to verify, this should never 160 happen, we should pass the JSON reply to the application */ 161 gcresp.hr.ec = TALER_JSON_get_error_code (j); 162 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 163 break; 164 case MHD_HTTP_INTERNAL_SERVER_ERROR: 165 /* Server had an internal issue; we should retry, but this API 166 leaves this to the application */ 167 gcresp.hr.ec = TALER_JSON_get_error_code (j); 168 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 169 break; 170 default: 171 /* unexpected response code */ 172 GNUNET_break_op (0); 173 gcresp.hr.ec = TALER_JSON_get_error_code (j); 174 gcresp.hr.hint = TALER_JSON_get_error_hint (j); 175 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 176 "Unexpected response code %u/%d for GET %s\n", 177 (unsigned int) response_code, 178 (int) gcresp.hr.ec, 179 cgh->url); 180 break; 181 } 182 if (NULL != cgh->cb) 183 { 184 cgh->cb (cgh->cb_cls, 185 &gcresp); 186 cgh->cb = NULL; 187 } 188 DONAU_charity_get_cancel (cgh); 189 } 190 191 192 struct DONAU_CharityGetHandle * 193 DONAU_charity_get ( 194 struct GNUNET_CURL_Context *ctx, 195 const char *url, 196 const uint64_t id, 197 const struct DONAU_CharityPrivateKeyP *charity_priv, 198 DONAU_GetCharityResponseCallback cb, 199 void *cb_cls) 200 { 201 struct DONAU_CharityGetHandle *cgh; 202 CURL *eh; 203 char arg_str[sizeof (id) * 2 + 32]; 204 205 TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", 206 url); 207 cgh = GNUNET_new (struct DONAU_CharityGetHandle); 208 cgh->cb = cb; 209 cgh->charity_id = id; 210 cgh->cb_cls = cb_cls; 211 GNUNET_snprintf (arg_str, 212 sizeof (arg_str), 213 "charity/%llu", 214 (unsigned long long) 215 id); 216 cgh->url = TALER_url_join (url, 217 arg_str, 218 NULL); 219 if (NULL == cgh->url) 220 { 221 GNUNET_free (cgh); 222 return NULL; 223 } 224 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 225 "Requesting a charity with URL `%s'.\n", 226 cgh->url); 227 eh = DONAU_curl_easy_get_ (cgh->url); 228 if (NULL == eh) 229 { 230 GNUNET_break (0); 231 GNUNET_free (cgh->url); 232 GNUNET_free (cgh); 233 return NULL; 234 } 235 cgh->job = GNUNET_CURL_job_add_with_ct_json (ctx, 236 eh, 237 &handle_charity_get_finished, 238 cgh); 239 GNUNET_assert (NULL != cgh->job); 240 { 241 struct DONAU_CharitySignatureP charity_sig; 242 char *sig_hdr; 243 char *hdr; 244 struct curl_slist *auth; 245 246 DONAU_charity_get_info_sign (charity_priv, 247 &charity_sig); 248 249 sig_hdr = GNUNET_STRINGS_data_to_string_alloc ( 250 &charity_sig, 251 sizeof (charity_sig)); 252 GNUNET_asprintf (&hdr, 253 "%s: %s", 254 DONAU_HTTP_HEADER_CHARITY_SIGNATURE, 255 sig_hdr); 256 GNUNET_free (sig_hdr); 257 auth = curl_slist_append (NULL, 258 hdr); 259 GNUNET_free (hdr); 260 GNUNET_CURL_extend_headers (cgh->job, 261 auth); 262 curl_slist_free_all (auth); 263 } 264 265 return cgh; 266 } 267 268 269 void 270 DONAU_charity_get_cancel ( 271 struct DONAU_CharityGetHandle *cgh) 272 { 273 if (NULL != cgh->job) 274 { 275 GNUNET_CURL_job_cancel (cgh->job); 276 cgh->job = NULL; 277 } 278 GNUNET_free (cgh->url); 279 GNUNET_free (cgh); 280 }