donau_api_donation_statement_get.c (8509B)
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_donation_statement_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 /donation-statement/$YEAR/$HASH_DONOR_ID request. 34 */ 35 struct DONAU_DonationStatementGetHandle 36 { 37 /** 38 * The url for the /donation-statement/$YEAR/$HASH_DONOR_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_GetDonationStatmentResponseCallback cb; 51 52 /** 53 * Salted and hashed donor id 54 */ 55 struct DONAU_HashDonorTaxId h_donor_tax_id; 56 57 /** 58 * year 59 */ 60 uint64_t year; 61 62 /** 63 * Closure to pass to @e cb. 64 */ 65 void *cb_cls; 66 67 }; 68 69 70 /** 71 * Callback used when downloading the reply to a /donation-statement/$YEAR/$HASH_DONOR_ID request 72 * is complete. 73 * 74 * @param cls the `struct KeysRequest` 75 * @param response_code HTTP response code, 0 on error 76 * @param resp_obj parsed JSON result, NULL on error 77 */ 78 static void 79 handle_donation_statement_get_finished (void *cls, 80 long response_code, 81 const void *resp_obj) 82 { 83 struct DONAU_DonationStatementGetHandle *dsgh = cls; 84 const json_t *j = resp_obj; 85 struct DONAU_DonationStatementResponse dsresp = { 86 .hr.reply = j, 87 .hr.http_status = (unsigned int) response_code 88 }; 89 90 dsgh->job = NULL; 91 switch (response_code) 92 { 93 case 0: 94 dsresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 95 break; 96 case MHD_HTTP_OK: 97 { 98 struct GNUNET_JSON_Specification spec[] = { 99 GNUNET_JSON_spec_fixed_auto ( 100 "donation_statement_sig", 101 &dsresp.details.ok.donation_statement_sig), 102 TALER_JSON_spec_amount_any ("total", 103 &dsresp.details.ok.total_amount), 104 GNUNET_JSON_spec_fixed_auto ("donau_pub", 105 &dsresp.details.ok.donau_pub), 106 GNUNET_JSON_spec_end () 107 }; 108 109 if (GNUNET_OK != 110 GNUNET_JSON_parse (j, 111 spec, 112 NULL, 113 NULL)) 114 { 115 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 116 "Could not parse response from donation-statement GET\n"); 117 GNUNET_break_op (0); 118 dsresp.hr.http_status = 0; 119 dsresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 120 } 121 else if (GNUNET_OK != 122 DONAU_donation_statement_verify ( 123 &dsresp.details.ok.total_amount, 124 (uint32_t) dsgh->year, 125 &dsgh->h_donor_tax_id, 126 &dsresp.details.ok.donau_pub, 127 &dsresp.details.ok.donation_statement_sig)) 128 { 129 /* The donau returned a total/signature that does not verify under 130 the returned donau public key. NOTE: the caller must still 131 cross-check donau_pub against the trusted signing keys from 132 /keys; this only rejects internally-inconsistent replies. */ 133 GNUNET_break_op (0); 134 dsresp.hr.http_status = 0; 135 dsresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 136 } 137 dsgh->cb (dsgh->cb_cls, 138 &dsresp); 139 dsgh->cb = NULL; 140 break; 141 } 142 case MHD_HTTP_BAD_REQUEST: 143 /* This should never happen, either us or the donau is buggy 144 (or API version conflict); just pass JSON reply to the application */ 145 dsresp.hr.ec = TALER_JSON_get_error_code (j); 146 dsresp.hr.hint = TALER_JSON_get_error_hint (j); 147 break; 148 case MHD_HTTP_NOT_FOUND: 149 /* Nothing really to verify, this should never 150 happen, we should pass the JSON reply to the application */ 151 dsresp.hr.ec = TALER_JSON_get_error_code (j); 152 dsresp.hr.hint = TALER_JSON_get_error_hint (j); 153 break; 154 case MHD_HTTP_NO_CONTENT: 155 /* No donation statement exists for the given year and donor; 156 this is a normal, non-error outcome (empty body). */ 157 break; 158 case MHD_HTTP_INTERNAL_SERVER_ERROR: 159 /* Server had an internal issue; we should retry, but this API 160 leaves this to the application */ 161 dsresp.hr.ec = TALER_JSON_get_error_code (j); 162 dsresp.hr.hint = TALER_JSON_get_error_hint (j); 163 break; 164 case MHD_HTTP_SERVICE_UNAVAILABLE: 165 /* The donau is lacking the keys to create the donation statement. */ 166 dsresp.hr.ec = TALER_JSON_get_error_code (j); 167 dsresp.hr.hint = TALER_JSON_get_error_hint (j); 168 break; 169 default: 170 /* unexpected response code */ 171 GNUNET_break_op (0); 172 dsresp.hr.ec = TALER_JSON_get_error_code (j); 173 dsresp.hr.hint = TALER_JSON_get_error_hint (j); 174 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 175 "Unexpected response code %u/%d for GET %s\n", 176 (unsigned int) response_code, 177 (int) dsresp.hr.ec, 178 dsgh->url); 179 break; 180 } 181 if (NULL != dsgh->cb) 182 { 183 dsgh->cb (dsgh->cb_cls, 184 &dsresp); 185 dsgh->cb = NULL; 186 } 187 DONAU_donation_statement_get_cancel (dsgh); 188 } 189 190 191 /** 192 * Prepares the request URL for the donation-statement-get request 193 * 194 * @param dsgh The handler 195 * @param donau_url The base-URL to the donau 196 */ 197 static 198 enum GNUNET_GenericReturnValue 199 prepare_url ( 200 struct DONAU_DonationStatementGetHandle *dsgh, 201 const char *donau_url) 202 { 203 char arg_str[sizeof (struct DONAU_HashDonorTaxId) * 2 + 32]; 204 char donor_id_hash_str[sizeof (struct DONAU_HashDonorTaxId) * 2]; 205 char *end; 206 207 end = GNUNET_STRINGS_data_to_string ( 208 &dsgh->h_donor_tax_id, 209 sizeof (struct DONAU_HashDonorTaxId), 210 donor_id_hash_str, 211 sizeof (donor_id_hash_str)); 212 *end = '\0'; 213 GNUNET_snprintf (arg_str, 214 sizeof (arg_str), 215 "donation-statement/%llu/%s", 216 (unsigned long long) dsgh->year, 217 donor_id_hash_str); 218 219 dsgh->url = TALER_url_join (donau_url, 220 arg_str, 221 NULL); 222 if (NULL == dsgh->url) 223 { 224 GNUNET_break (0); 225 DONAU_donation_statement_get_cancel (dsgh); 226 return GNUNET_SYSERR; 227 } 228 229 return GNUNET_OK; 230 } 231 232 233 struct DONAU_DonationStatementGetHandle * 234 DONAU_donation_statement_get ( 235 struct GNUNET_CURL_Context *ctx, 236 const char *url, 237 const uint64_t year, 238 const struct DONAU_HashDonorTaxId *h_donor_tax_id, 239 DONAU_GetDonationStatmentResponseCallback cb, 240 void *cb_cls) 241 { 242 struct DONAU_DonationStatementGetHandle *dsgh; 243 CURL *eh; 244 245 TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", 246 url); 247 248 dsgh = GNUNET_new (struct DONAU_DonationStatementGetHandle); 249 dsgh->cb = cb; 250 dsgh->cb_cls = cb_cls; 251 dsgh->year = year; 252 dsgh->h_donor_tax_id = *h_donor_tax_id; 253 if (GNUNET_OK != prepare_url (dsgh, 254 url)) 255 return NULL; 256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 257 "Requesting a charity with URL `%s'.\n", 258 dsgh->url); 259 eh = DONAU_curl_easy_get_ (dsgh->url); 260 if (NULL == eh) 261 { 262 GNUNET_break (0); 263 GNUNET_free (dsgh->url); 264 GNUNET_free (dsgh); 265 return NULL; 266 } 267 dsgh->job = GNUNET_CURL_job_add_with_ct_json (ctx, 268 eh, 269 & 270 handle_donation_statement_get_finished, 271 dsgh); 272 return dsgh; 273 } 274 275 276 void 277 DONAU_donation_statement_get_cancel ( 278 struct DONAU_DonationStatementGetHandle *dsgh) 279 { 280 if (NULL != dsgh->job) 281 { 282 GNUNET_CURL_job_cancel (dsgh->job); 283 dsgh->job = NULL; 284 } 285 GNUNET_free (dsgh->url); 286 GNUNET_free (dsgh); 287 }