bank_api_post_accounts_withdrawals_confirm.c (10837B)
1 /* 2 This file is part of TALER cash2ecash 3 Copyright (C) 2026 GNUnet e.V. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation, either version 3 of the 8 License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 /** 20 * @file lib/bank_api_post_accounts_withdrawals_confirm.c 21 * @brief implements the Taler Bank API "POST /accounts/$USERNAME/withdrawals/$WITHDRAWAL_ID/confirm" handler 22 * @author Reto Tellenbach 23 */ 24 25 #include <microhttpd.h> 26 #include "taler/taler_json_lib.h" 27 #include "bank_api_post_accounts_withdrawals_confirm.h" 28 29 /** 30 * Log error related to CURL operations. 31 * 32 * @param type log level 33 * @param function which function failed to run 34 * @param code what was the curl error code 35 */ 36 #define CURL_STRERROR(type, function, code) \ 37 GNUNET_log (type, \ 38 "Curl function `%s' has failed at `%s:%d' with error: %s", \ 39 function, __FILE__, __LINE__, curl_easy_strerror (code)); 40 41 /** 42 * Handle for the accounts create withdrawal request. 43 */ 44 struct TALER_BANK_PostWithdrawalConfirmHandle 45 { 46 /** 47 * The context of this handle 48 */ 49 struct GNUNET_CURL_Context *ctx; 50 51 /** 52 * curle easy handle 53 */ 54 CURL *easy_handle; 55 56 /** 57 * Context for curl easy post. Keeps the data that must 58 * persist for Curl to make the upload. 59 */ 60 struct TALER_CURL_PostContext post_ctx; 61 62 /** 63 * Authentification date to access bank account 64 */ 65 const struct DIGITIZER_BankAuthenticationData *authorization; 66 67 /** 68 * Function to call with the , 69 * NULL if this has already been done. 70 */ 71 TALER_BANK_WithdrawalConfirmCallback woc_cb; 72 73 /** 74 * Closure to pass to 75 * ater withdrawal operation creation 76 */ 77 void *woc_cb_cls; 78 79 /** 80 * Data for the request to get the accounts/$USERNAME of a bank, 81 * NULL once we are past stage #MHS_INIT. 82 */ 83 struct GNUNET_CURL_Job *job; 84 85 /** 86 * The whole request line 87 */ 88 char *job_url; 89 90 }; 91 92 93 /** 94 * Decode the JSON in @a resp_obj from the accounts/$USERNAME/withdrawal/confirm response when challenged 95 * 96 * @param[in] resp_obj JSON object to parse 97 * @param[in,out] vi where to store the results we decoded 98 * @param[out] vc where to store challenge information 99 * @return #TALER_EC_NONE on success 100 */ 101 static enum TALER_ErrorCode 102 decode_challenge_json (const json_t *resp_obj, 103 struct TALER_BANK_ChallengeResponse *vi) 104 { 105 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 106 "Received body\n`%s'\n", 107 json_dumps(resp_obj, 0)); 108 109 110 struct GNUNET_JSON_Specification spec[] = { 111 GNUNET_JSON_spec_json ("balance", 112 &vi->blc), 113 GNUNET_JSON_spec_end () 114 }; 115 116 if (JSON_OBJECT != json_typeof (resp_obj)) 117 { 118 GNUNET_break_op (0); 119 return TALER_EC_GENERIC_JSON_INVALID; 120 } 121 if (GNUNET_OK != 122 GNUNET_JSON_parse (resp_obj, 123 spec, 124 NULL, NULL)) 125 { 126 GNUNET_break_op (0); 127 return TALER_EC_GENERIC_JSON_INVALID; 128 } 129 130 return TALER_EC_NONE; 131 } 132 133 134 /** 135 * Callback used when http reply arived to a /accounts/$USERNAME/withdrawal/confirm request. 136 * 137 * @param cls the `struct TALER_BANK_PostWithdrawalConfirmHandle` 138 * @param response_code HTTP response code or 0 on error 139 * @param gresp_obj JSON result, NULL on error, must be a `const json_t *` 140 */ 141 static void 142 response_cb(void *cls, 143 long response_code, 144 const void *gresp_obj) 145 { 146 struct TALER_BANK_PostWithdrawalConfirmHandle *pcwh = cls; 147 const json_t *resp_obj = gresp_obj; 148 149 struct TALER_BANK_WithdrawalConfirmResponse pacwr = { 150 .hr.response = resp_obj, 151 .hr.http_status = (unsigned int)response_code 152 }; 153 154 pcwh->job = NULL; //job was successfull, curl job cancel not needed anymore in cleanup 155 pcwh->easy_handle = NULL; 156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 157 "Received from URL `%s' with status %ld.\n", 158 pcwh->job_url, 159 response_code); 160 161 switch (response_code) 162 { 163 case 0: 164 GNUNET_break_op (0); 165 pacwr.hr.ec = TALER_EC_INVALID; 166 break; 167 case MHD_HTTP_ACCEPTED: 168 if (NULL == resp_obj) 169 { 170 GNUNET_break_op (0); 171 pacwr.hr.http_status = 0; 172 break; 173 } 174 pacwr.hr.ec = decode_challenge_json (resp_obj, 175 &pacwr.details.ok.challenge); 176 if (TALER_EC_NONE != pacwr.hr.ec) 177 { 178 GNUNET_break_op (0); 179 pacwr.hr.http_status = 0; 180 break; 181 } 182 break; 183 case MHD_HTTP_OK: 184 if (NULL == resp_obj) 185 { 186 GNUNET_break_op (0); 187 pacwr.hr.http_status = 0; 188 pacwr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 189 break; 190 } 191 if (TALER_EC_NONE != pacwr.hr.ec) 192 { 193 GNUNET_break_op (0); 194 pacwr.hr.http_status = 0; 195 break; 196 } 197 break; 198 case MHD_HTTP_UNAUTHORIZED: 199 pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); 200 pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 201 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 202 "Invalid or missing credentials %u/%d\n", 203 (unsigned int) response_code, 204 (int) pacwr.hr.ec); 205 break; 206 case MHD_HTTP_FORBIDDEN: 207 pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); 208 pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 209 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 210 "Missing rights %u/%d\n", 211 (unsigned int) response_code, 212 (int) pacwr.hr.ec); 213 break; 214 case MHD_HTTP_NOT_FOUND: 215 pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); 216 pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 217 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 218 "The operation was not found. %u/%d\n", 219 (unsigned int) response_code, 220 (int) pacwr.hr.ec); 221 break; 222 case MHD_HTTP_CONFLICT: 223 pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); 224 pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 225 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 226 "Conflict %u/%d\n", 227 (unsigned int) response_code, 228 (int) pacwr.hr.ec); 229 break; 230 case MHD_HTTP_INTERNAL_SERVER_ERROR: 231 pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); 232 pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 233 break; 234 default: 235 pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); 236 pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 237 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 238 "Unexpected response code %u/%d\n", 239 (unsigned int) response_code, 240 (int) pacwr.hr.ec); 241 break; 242 } 243 244 pcwh->woc_cb (pcwh->woc_cb_cls, &pacwr); 245 TALER_BANK_post_withdrawal_confirm_cancel(pcwh); 246 } 247 248 /** 249 * create handle for poost accounts/withdrawal/confirm request 250 */ 251 struct TALER_BANK_PostWithdrawalConfirmHandle * 252 TALER_BANK_post_withdrawal_confirm_create ( struct GNUNET_CURL_Context *ctx, 253 const char *base_url, 254 const char *username, 255 const char *wopid, 256 const struct DIGITIZER_BankAuthenticationData *authorization) 257 { 258 struct TALER_BANK_PostWithdrawalConfirmHandle *pwc; 259 char *usr; 260 pwc = GNUNET_new(struct TALER_BANK_PostWithdrawalConfirmHandle); 261 262 pwc->ctx = ctx; 263 pwc->authorization = authorization; 264 265 GNUNET_asprintf(&usr, 266 "accounts/%s/withdrawals/%s/confirm", 267 username, 268 wopid); 269 pwc->job_url = TALER_url_join(base_url, 270 usr, 271 NULL); 272 pwc->easy_handle = TALER_BANK_curl_easy_get_ (pwc->job_url); 273 if (NULL == pwc->easy_handle) 274 { 275 GNUNET_free (usr); 276 GNUNET_break (0); 277 TALER_BANK_post_withdrawal_confirm_cancel (pwc); 278 return GNUNET_NO; 279 } 280 GNUNET_free (usr); 281 return pwc; 282 } 283 284 /** 285 * Post accounts/withdrawal 286 */ 287 enum GNUNET_GenericReturnValue 288 TALER_BANK_post_withdrawal_confirm ( struct TALER_BANK_PostWithdrawalConfirmHandle *handle, 289 const struct TALER_BANK_AccountWithdrawalConfirmRequest*req_ctx, 290 TALER_BANK_WithdrawalConfirmCallback pwc_cb, 291 void *pwc_cb_cls) 292 { 293 json_t *req; 294 295 handle->woc_cb = pwc_cb; 296 handle->woc_cb_cls = pwc_cb_cls; 297 298 req = GNUNET_JSON_PACK ( 299 GNUNET_JSON_pack_allow_null ((TALER_amount_is_valid(&req_ctx->amount) & 300 !TALER_amount_is_zero(&req_ctx->amount)) ? 301 TALER_JSON_pack_amount ("amount", 302 &req_ctx->amount): 303 GNUNET_JSON_pack_string ("amount", NULL))); 304 305 306 if(GNUNET_OK != DIGITIZER_setup_auth_(handle->easy_handle,handle->authorization)) 307 { 308 GNUNET_break(0); 309 TALER_BANK_post_withdrawal_confirm_cancel(handle); 310 return GNUNET_NO; 311 } 312 if(GNUNET_OK != 313 TALER_curl_easy_post(&handle->post_ctx, 314 handle->easy_handle, 315 req)) 316 { 317 GNUNET_break(0); 318 TALER_BANK_post_withdrawal_confirm_cancel(handle); 319 return GNUNET_NO; 320 } 321 322 json_decref(req); 323 GNUNET_log( GNUNET_ERROR_TYPE_INFO, 324 "Requesting URL `%s'.\n", 325 handle->job_url); 326 handle->job = GNUNET_CURL_job_add2(handle->ctx, 327 handle->easy_handle, 328 handle->post_ctx.headers, 329 &response_cb, 330 handle); 331 if(NULL == handle->job) 332 { 333 GNUNET_break(0); 334 TALER_BANK_post_withdrawal_confirm_cancel(handle); 335 return GNUNET_NO; 336 } 337 338 return GNUNET_OK; 339 } 340 341 342 343 void 344 TALER_BANK_post_withdrawal_confirm_cancel ( struct TALER_BANK_PostWithdrawalConfirmHandle *pwch) 345 { 346 if(NULL != pwch->job) 347 { 348 GNUNET_CURL_job_cancel(pwch->job); 349 pwch->job = NULL; 350 pwch->easy_handle = NULL; 351 } 352 if(NULL != pwch->easy_handle) 353 { 354 curl_easy_cleanup (pwch->easy_handle); 355 pwch->easy_handle = NULL; 356 } 357 TALER_curl_easy_post_finished(&pwch->post_ctx); 358 GNUNET_free(pwch->job_url); 359 GNUNET_free(pwch); 360 }