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