bank_api_get_withdrawals.c (8807B)
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 bank-lib/bank_api_get_withdrawals.c 21 * @brief implements the Taler Bank API "GET /withdrawals/$WITHDRAWAL_ID" handler 22 * @author Reto Tellenbach 23 */ 24 25 #include <microhttpd.h> 26 #include "taler/taler_json_lib.h" 27 #include "taler/taler_bank_service.h" 28 #include "bank_api_get_withdrawals.h" 29 #include "bank_api_curl_defaults.h" 30 31 /** 32 * Log error related to CURL operations. 33 * 34 * @param type log level 35 * @param function which function failed to run 36 * @param code what was the curl error code 37 */ 38 #define CURL_STRERROR(type, function, code) \ 39 GNUNET_log (type, \ 40 "Curl function `%s' has failed at `%s:%d' with error: %s", \ 41 function, __FILE__, __LINE__, curl_easy_strerror (code)); 42 43 /** 44 * Handle for the accounts request. 45 */ 46 struct TALER_BANK_GetWithdrawalHandle 47 { 48 /** 49 * The context of this handle 50 */ 51 struct GNUNET_CURL_Context *ctx; 52 53 /** 54 * Function to call with the , 55 * NULL if this has already been done. 56 */ 57 TALER_BANK_WithdrawalCallback withdrawals_cb; 58 59 /** 60 * Closure to pass to 61 */ 62 void *withdrawals_cb_cls; 63 64 /** 65 * Data for the request to get the /withdrawals/$WITHDRAWAL_ID of a bank, 66 * NULL once we are past stage #MHS_INIT. 67 */ 68 struct GNUNET_CURL_Job *job; 69 70 /** 71 * The whole request line 72 */ 73 char *job_url; 74 }; 75 76 77 /** 78 * Decode the JSON in @a resp_obj from the /withdrawals/$WITHDRAWAL_ID response 79 * 80 * @param[in] resp_obj JSON object to parse 81 * @param[in,out] vi where to store the results we decoded 82 * @param[out] vc where to store account info data 83 * @return #TALER_EC_NONE on success 84 */ 85 static enum TALER_ErrorCode 86 decode_withdrawals_json (const json_t *resp_obj, 87 struct TALER_BANK_WithdrawalInfo *vi) 88 { 89 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 90 "Received body\n`%s'\n", 91 json_dumps(resp_obj, 0)); 92 93 94 struct GNUNET_JSON_Specification spec[] = { 95 GNUNET_JSON_spec_mark_optional ( 96 GNUNET_JSON_spec_string ("status", 97 &vi->status), 98 NULL), 99 GNUNET_JSON_spec_mark_optional ( 100 TALER_JSON_spec_amount_any ("amount", 101 &vi->amount),NULL), 102 GNUNET_JSON_spec_mark_optional ( 103 TALER_JSON_spec_amount_any ("suggested_amount", 104 &vi->suggested_amount),NULL), 105 GNUNET_JSON_spec_mark_optional ( 106 GNUNET_JSON_spec_bool ("no_amount_to_wallet", 107 &vi->no_amount_to_wallet),NULL), 108 GNUNET_JSON_spec_string ("username", 109 &vi->username), 110 GNUNET_JSON_spec_mark_optional ( 111 GNUNET_JSON_spec_string ("selected_reserve_pub", 112 &vi->selected_reserve_pub),NULL), 113 GNUNET_JSON_spec_mark_optional ( 114 GNUNET_JSON_spec_string ("selected_exchange_account", 115 &vi->selected_exchange_account),NULL), 116 GNUNET_JSON_spec_end () 117 }; 118 119 if (JSON_OBJECT != json_typeof (resp_obj)) 120 { 121 GNUNET_break_op (0); 122 return TALER_EC_GENERIC_JSON_INVALID; 123 } 124 if (GNUNET_OK != 125 GNUNET_JSON_parse (resp_obj, 126 spec, 127 NULL, NULL)) 128 { 129 GNUNET_break_op (0); 130 return TALER_EC_GENERIC_JSON_INVALID; 131 } 132 133 return TALER_EC_NONE; 134 } 135 136 137 /** 138 * Callback used when http reply arived to a /withdrawals/$WITHDRAWAL_ID request. 139 * 140 * @param cls the `struct TALER_BANK_GetWithdrawalHandle` 141 * @param response_code HTTP response code or 0 on error 142 * @param gresp_obj JSON result, NULL on error, must be a `const json_t *` 143 */ 144 static void 145 response_cb(void *cls, 146 long response_code, 147 const void *gresp_obj) 148 { 149 struct TALER_BANK_GetWithdrawalHandle *withdrawal = cls; 150 const json_t *resp_obj = gresp_obj; 151 152 struct TALER_BANK_WithdrawalResponse wr = { 153 .hr.response = resp_obj, 154 .hr.http_status = (unsigned int)response_code 155 }; 156 157 withdrawal->job = NULL; //job was successfull, curl job cancel not needed anymore in cleanup 158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 159 "Received withdrawal info from URL `%s' with status %ld.\n", 160 withdrawal->job_url, 161 response_code); 162 163 switch (response_code) 164 { 165 case 0: 166 GNUNET_break_op (0); 167 wr.hr.ec = TALER_EC_INVALID; 168 break; 169 case MHD_HTTP_OK: 170 if (NULL == resp_obj) 171 { 172 GNUNET_break_op (0); 173 wr.hr.http_status = 0; 174 wr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 175 break; 176 } 177 wr.hr.ec = decode_withdrawals_json (resp_obj, 178 &wr.details.ok.acc); 179 if (TALER_EC_NONE != wr.hr.ec) 180 { 181 GNUNET_break_op (0); 182 wr.hr.http_status = 0; 183 break; 184 } 185 break; 186 case MHD_HTTP_NOT_FOUND: 187 wr.hr.ec = TALER_JSON_get_error_code (resp_obj); 188 wr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 189 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 190 "The operation was not found %u/%d\n", 191 (unsigned int) response_code, 192 (int) wr.hr.ec); 193 break; 194 case MHD_HTTP_INTERNAL_SERVER_ERROR: 195 wr.hr.ec = TALER_JSON_get_error_code (resp_obj); 196 wr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 197 break; 198 default: 199 wr.hr.ec = TALER_JSON_get_error_code (resp_obj); 200 wr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 201 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 202 "Unexpected response code %u/%d\n", 203 (unsigned int) response_code, 204 (int) wr.hr.ec); 205 break; 206 } 207 208 withdrawal->withdrawals_cb (withdrawal->withdrawals_cb_cls, &wr); 209 TALER_BANK_get_withdrawal_cancel(withdrawal); 210 } 211 212 213 struct TALER_BANK_GetWithdrawalHandle * 214 TALER_BANK_get_withdrawal ( struct GNUNET_CURL_Context *ctx, 215 const char *base_url, 216 const char *wopid, 217 TALER_BANK_WithdrawalCallback withdrawals_cb, 218 void *withdrawals_cb_cls, 219 const char *timeout_ms) 220 { 221 struct TALER_BANK_GetWithdrawalHandle *withdrawal; 222 char *url; 223 CURL *eh; 224 225 withdrawal = GNUNET_new(struct TALER_BANK_GetWithdrawalHandle); 226 withdrawal->withdrawals_cb = withdrawals_cb; 227 withdrawal->withdrawals_cb_cls = withdrawals_cb_cls; 228 withdrawal->ctx = ctx; 229 GNUNET_asprintf(&url, 230 "withdrawals/%s", 231 wopid); 232 if (NULL != timeout_ms) 233 { 234 withdrawal->job_url = TALER_url_join(base_url, 235 url, 236 "timeout_ms", 237 timeout_ms, 238 NULL); 239 } 240 else 241 { 242 withdrawal->job_url = TALER_url_join(base_url, 243 url, 244 NULL); 245 } 246 if(NULL == withdrawal->job_url) 247 { 248 GNUNET_break(0); 249 GNUNET_free(withdrawal); 250 return NULL; 251 } 252 GNUNET_log( GNUNET_ERROR_TYPE_INFO, 253 "Requesting bank account information with URL `%s'.\n", 254 withdrawal->job_url); 255 eh = TALER_BANK_curl_easy_get_(withdrawal->job_url); 256 if(NULL == eh) 257 { 258 GNUNET_break(0); 259 TALER_BANK_get_withdrawal_cancel(withdrawal); 260 return NULL; 261 } 262 withdrawal->job = 263 GNUNET_CURL_job_add(withdrawal->ctx, 264 eh, 265 &response_cb, 266 withdrawal); 267 if(NULL == withdrawal->job) 268 { 269 GNUNET_break(0); 270 TALER_BANK_get_withdrawal_cancel(withdrawal); 271 return NULL; 272 } 273 274 return withdrawal; 275 } 276 277 278 279 void 280 TALER_BANK_get_withdrawal_cancel ( struct TALER_BANK_GetWithdrawalHandle *withdrawal) 281 { 282 if(NULL != withdrawal->job) 283 { 284 GNUNET_CURL_job_cancel(withdrawal->job); 285 withdrawal->job = NULL; 286 } 287 GNUNET_free(withdrawal->job_url); 288 GNUNET_free(withdrawal); 289 }