lae_credit.c (9615B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2017--2021 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License 7 as published by the Free Software Foundation; either version 3, 8 or (at your option) any later version. 9 10 Anastasis is distributed in the hope that it will be useful, 11 but 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 Anastasis; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file libanastasiseufin/lae_credit.c 21 * @brief Implementation of the /history/incoming 22 * requests of the libeufin's Anastasis facade 23 * @author Christian Grothoff 24 * @author Marcello Stanisci 25 */ 26 #include "platform.h" 27 #include "lae_common.h" 28 #include <microhttpd.h> /* just for HTTP status codes */ 29 30 31 /** 32 * @brief A /history/incoming Handle 33 */ 34 struct ANASTASIS_EUFIN_CreditHistoryHandle 35 { 36 37 /** 38 * The url for this request. 39 */ 40 char *request_url; 41 42 /** 43 * Handle for the request. 44 */ 45 struct GNUNET_CURL_Job *job; 46 47 /** 48 * Function to call with the result. 49 */ 50 ANASTASIS_EUFIN_CreditHistoryCallback hcb; 51 52 /** 53 * Closure for @a cb. 54 */ 55 void *hcb_cls; 56 }; 57 58 59 /** 60 * Parse history given in JSON format and invoke the callback on each item. 61 * 62 * @param hh handle to the account history request 63 * @param history JSON array with the history 64 * @return #GNUNET_OK if history was valid and @a rhistory and @a balance 65 * were set, 66 * #GNUNET_SYSERR if there was a protocol violation in @a history 67 */ 68 static enum GNUNET_GenericReturnValue 69 parse_account_history (struct ANASTASIS_EUFIN_CreditHistoryHandle *hh, 70 const json_t *history) 71 { 72 json_t *history_array; 73 74 if (NULL == (history_array = json_object_get (history, 75 "incoming_transactions"))) 76 { 77 GNUNET_break_op (0); 78 return GNUNET_SYSERR; 79 } 80 if (! json_is_array (history_array)) 81 { 82 GNUNET_break_op (0); 83 return GNUNET_SYSERR; 84 } 85 for (size_t i = 0; i<json_array_size (history_array); i++) 86 { 87 struct ANASTASIS_EUFIN_CreditDetails td; 88 uint64_t row_id; 89 struct GNUNET_JSON_Specification hist_spec[] = { 90 TALER_JSON_spec_amount_any ("amount", 91 &td.amount), 92 GNUNET_JSON_spec_timestamp ("date", 93 &td.execution_date), 94 GNUNET_JSON_spec_uint64 ("row_id", 95 &row_id), 96 GNUNET_JSON_spec_string ("subject", 97 &td.wire_subject), 98 GNUNET_JSON_spec_string ("debit_account", 99 &td.debit_account_uri), 100 GNUNET_JSON_spec_string ("credit_account", 101 &td.credit_account_uri), 102 GNUNET_JSON_spec_end () 103 }; 104 json_t *transaction = json_array_get (history_array, 105 i); 106 107 if (GNUNET_OK != 108 GNUNET_JSON_parse (transaction, 109 hist_spec, 110 NULL, NULL)) 111 { 112 GNUNET_break_op (0); 113 return GNUNET_SYSERR; 114 } 115 if (GNUNET_OK != 116 hh->hcb (hh->hcb_cls, 117 MHD_HTTP_OK, 118 TALER_EC_NONE, 119 row_id, 120 &td)) 121 { 122 hh->hcb = NULL; 123 GNUNET_JSON_parse_free (hist_spec); 124 return GNUNET_OK; 125 } 126 GNUNET_JSON_parse_free (hist_spec); 127 } 128 return GNUNET_OK; 129 } 130 131 132 /** 133 * Function called when we're done processing the 134 * HTTP /history/incoming request. 135 * 136 * @param cls the `struct ANASTASIS_EUFIN_CreditHistoryHandle` 137 * @param response_code HTTP response code, 0 on error 138 * @param response parsed JSON result, NULL on error 139 */ 140 static void 141 handle_credit_history_finished (void *cls, 142 long response_code, 143 const void *response) 144 { 145 struct ANASTASIS_EUFIN_CreditHistoryHandle *hh = cls; 146 const json_t *j = response; 147 enum TALER_ErrorCode ec; 148 149 hh->job = NULL; 150 switch (response_code) 151 { 152 case 0: 153 ec = TALER_EC_GENERIC_INVALID_RESPONSE; 154 break; 155 case MHD_HTTP_OK: 156 if (GNUNET_OK != 157 parse_account_history (hh, 158 j)) 159 { 160 GNUNET_break_op (0); 161 response_code = 0; 162 ec = TALER_EC_GENERIC_INVALID_RESPONSE; 163 break; 164 } 165 response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */ 166 ec = TALER_EC_NONE; 167 break; 168 case MHD_HTTP_NO_CONTENT: 169 ec = TALER_EC_NONE; 170 break; 171 case MHD_HTTP_BAD_REQUEST: 172 /* This should never happen, either us or the bank is buggy 173 (or API version conflict); just pass JSON reply to the application */ 174 GNUNET_break_op (0); 175 ec = TALER_JSON_get_error_code (j); 176 break; 177 case MHD_HTTP_UNAUTHORIZED: 178 /* Nothing really to verify, bank says the HTTP Authentication 179 failed. May happen if HTTP authentication is used and the 180 user supplied a wrong username/password combination. */ 181 ec = TALER_JSON_get_error_code (j); 182 break; 183 case MHD_HTTP_NOT_FOUND: 184 /* Nothing really to verify: the bank is either unaware 185 of the endpoint (not a bank), or of the account. 186 We should pass the JSON (?) reply to the application */ 187 ec = TALER_JSON_get_error_code (j); 188 break; 189 case MHD_HTTP_INTERNAL_SERVER_ERROR: 190 /* Server had an internal issue; we should retry, but this API 191 leaves this to the application */ 192 ec = TALER_JSON_get_error_code (j); 193 break; 194 default: 195 /* unexpected response code */ 196 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 197 "Unexpected response code %u\n", 198 (unsigned int) response_code); 199 ec = TALER_JSON_get_error_code (j); 200 break; 201 } 202 if (NULL != hh->hcb) 203 hh->hcb (hh->hcb_cls, 204 response_code, 205 ec, 206 0LLU, 207 NULL); 208 ANASTASIS_EUFIN_credit_history_cancel (hh); 209 } 210 211 212 struct ANASTASIS_EUFIN_CreditHistoryHandle * 213 ANASTASIS_EUFIN_credit_history ( 214 struct GNUNET_CURL_Context *ctx, 215 const struct ANASTASIS_EUFIN_AuthenticationData *auth, 216 uint64_t start_row, 217 int64_t num_results, 218 struct GNUNET_TIME_Relative timeout, 219 ANASTASIS_EUFIN_CreditHistoryCallback hres_cb, 220 void *hres_cb_cls) 221 { 222 char url[128]; 223 struct ANASTASIS_EUFIN_CreditHistoryHandle *hh; 224 CURL *eh; 225 unsigned long long tms; 226 227 if (0 == num_results) 228 { 229 GNUNET_break (0); 230 return NULL; 231 } 232 233 tms = (unsigned long long) (timeout.rel_value_us 234 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); 235 if ( ( (UINT64_MAX == start_row) && 236 (0 > num_results) ) || 237 ( (0 == start_row) && 238 (0 < num_results) ) ) 239 { 240 if ( (0 < num_results) && 241 (! GNUNET_TIME_relative_is_zero (timeout)) ) 242 GNUNET_snprintf (url, 243 sizeof (url), 244 "history/incoming?delta=%lld&long_poll_ms=%llu", 245 (long long) num_results, 246 tms); 247 else 248 GNUNET_snprintf (url, 249 sizeof (url), 250 "history/incoming?delta=%lld", 251 (long long) num_results); 252 } 253 else 254 { 255 if ( (0 < num_results) && 256 (! GNUNET_TIME_relative_is_zero (timeout)) ) 257 GNUNET_snprintf (url, 258 sizeof (url), 259 "history/incoming?delta=%lld&start=%llu&long_poll_ms=%llu", 260 (long long) num_results, 261 (unsigned long long) start_row, 262 tms); 263 else 264 GNUNET_snprintf (url, 265 sizeof (url), 266 "history/incoming?delta=%lld&start=%llu", 267 (long long) num_results, 268 (unsigned long long) start_row); 269 } 270 hh = GNUNET_new (struct ANASTASIS_EUFIN_CreditHistoryHandle); 271 hh->hcb = hres_cb; 272 hh->hcb_cls = hres_cb_cls; 273 hh->request_url = TALER_url_join (auth->wire_gateway_url, 274 url, 275 NULL); 276 if (NULL == hh->request_url) 277 { 278 GNUNET_free (hh); 279 GNUNET_break (0); 280 return NULL; 281 } 282 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 283 "Requesting credit history at `%s'\n", 284 hh->request_url); 285 eh = curl_easy_init (); 286 if ( (NULL == eh) || 287 (GNUNET_OK != 288 ANASTASIS_EUFIN_setup_auth_ (eh, 289 auth)) || 290 (CURLE_OK != 291 curl_easy_setopt (eh, 292 CURLOPT_URL, 293 hh->request_url)) ) 294 { 295 GNUNET_break (0); 296 ANASTASIS_EUFIN_credit_history_cancel (hh); 297 if (NULL != eh) 298 curl_easy_cleanup (eh); 299 return NULL; 300 } 301 if (0 != tms) 302 { 303 GNUNET_break (CURLE_OK == 304 curl_easy_setopt (eh, 305 CURLOPT_TIMEOUT_MS, 306 (long) tms)); 307 } 308 hh->job = GNUNET_CURL_job_add2 (ctx, 309 eh, 310 NULL, 311 &handle_credit_history_finished, 312 hh); 313 return hh; 314 } 315 316 317 void 318 ANASTASIS_EUFIN_credit_history_cancel ( 319 struct ANASTASIS_EUFIN_CreditHistoryHandle *hh) 320 { 321 if (NULL != hh->job) 322 { 323 GNUNET_CURL_job_cancel (hh->job); 324 hh->job = NULL; 325 } 326 GNUNET_free (hh->request_url); 327 GNUNET_free (hh); 328 } 329 330 331 /* end of lae_credit.c */