merchant_api_get-private-transfers.c (12836B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Lesser General Public License as published by the Free Software 7 Foundation; either version 2.1, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License along with 14 TALER; see the file COPYING.LGPL. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file src/lib/merchant_api_get-private-transfers.c 19 * @brief Implementation of the GET /private/transfers request 20 * @author Christian Grothoff 21 */ 22 #include "platform.h" 23 #include <curl/curl.h> 24 #include <jansson.h> 25 #include <microhttpd.h> /* just for HTTP status codes */ 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_curl_lib.h> 28 #include <taler/merchant/get-private-transfers.h> 29 #include "merchant_api_curl_defaults.h" 30 #include <taler/taler_json_lib.h> 31 32 33 /** 34 * Maximum number of transfers we return. 35 */ 36 #define MAX_TRANSFERS 1024 37 38 39 /** 40 * Handle for a GET /private/transfers operation. 41 */ 42 struct TALER_MERCHANT_GetPrivateTransfersHandle 43 { 44 /** 45 * Base URL of the merchant backend. 46 */ 47 char *base_url; 48 49 /** 50 * The full URL for this request. 51 */ 52 char *url; 53 54 /** 55 * Handle for the request. 56 */ 57 struct GNUNET_CURL_Job *job; 58 59 /** 60 * Function to call with the result. 61 */ 62 TALER_MERCHANT_GetPrivateTransfersCallback cb; 63 64 /** 65 * Closure for @a cb. 66 */ 67 TALER_MERCHANT_GET_PRIVATE_TRANSFERS_RESULT_CLOSURE *cb_cls; 68 69 /** 70 * Reference to the execution context. 71 */ 72 struct GNUNET_CURL_Context *ctx; 73 74 /** 75 * Payto URI filter (URL-encoded), or NULL. 76 */ 77 char *payto_uri_enc; 78 79 /** 80 * Before timestamp filter, or GNUNET_TIME_UNIT_FOREVER_TS. 81 */ 82 struct GNUNET_TIME_Timestamp before; 83 84 /** 85 * After timestamp filter, or GNUNET_TIME_UNIT_ZERO_TS. 86 */ 87 struct GNUNET_TIME_Timestamp after; 88 89 /** 90 * Limit on number of results (0 = default). 91 */ 92 int64_t limit; 93 94 /** 95 * Offset for pagination. 96 */ 97 uint64_t offset; 98 99 /** 100 * Expected filter. 101 */ 102 enum TALER_EXCHANGE_YesNoAll expected; 103 104 /** 105 * True if offset was explicitly set. 106 */ 107 bool have_offset; 108 }; 109 110 111 /** 112 * Parse transfer information from @a transfers_arr. 113 * 114 * @param transfers_arr JSON array with transfer data 115 * @param[in,out] gtr response to fill 116 * @param gth operation handle 117 * @return #GNUNET_OK on success 118 */ 119 static enum GNUNET_GenericReturnValue 120 parse_transfers ( 121 const json_t *transfers_arr, 122 struct TALER_MERCHANT_GetPrivateTransfersResponse *gtr, 123 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth) 124 { 125 unsigned int tds_length = (unsigned int) json_array_size (transfers_arr); 126 127 if ( (json_array_size (transfers_arr) != (size_t) tds_length) || 128 (tds_length > MAX_TRANSFERS) ) 129 { 130 GNUNET_break (0); 131 return GNUNET_SYSERR; 132 } 133 { 134 struct TALER_MERCHANT_GetPrivateTransfersTransferData tds[ 135 GNUNET_NZL (tds_length)]; 136 size_t index; 137 json_t *value; 138 139 memset (tds, 140 0, 141 sizeof (tds)); 142 json_array_foreach (transfers_arr, index, value) { 143 struct TALER_MERCHANT_GetPrivateTransfersTransferData *td = &tds[index]; 144 struct GNUNET_JSON_Specification ispec[] = { 145 TALER_JSON_spec_amount_any ("credit_amount", 146 &td->credit_amount), 147 GNUNET_JSON_spec_fixed_auto ("wtid", 148 &td->wtid), 149 TALER_JSON_spec_full_payto_uri ("payto_uri", 150 &td->payto_uri), 151 /* Optional: unknown for transfers reported by an exchange 152 older than protocol v39. */ 153 GNUNET_JSON_spec_mark_optional ( 154 TALER_JSON_spec_full_payto_uri ("exchange_payto_uri", 155 &td->exchange_payto_uri), 156 NULL), 157 TALER_JSON_spec_web_url ("exchange_url", 158 &td->exchange_url), 159 GNUNET_JSON_spec_uint64 ("transfer_serial_id", 160 &td->transfer_serial_id), 161 GNUNET_JSON_spec_mark_optional ( 162 GNUNET_JSON_spec_timestamp ("execution_time", 163 &td->execution_time), 164 NULL), 165 GNUNET_JSON_spec_mark_optional ( 166 GNUNET_JSON_spec_uint64 ("expected_transfer_serial_id", 167 &td->expected_transfer_serial_id), 168 NULL), 169 GNUNET_JSON_spec_bool ("expected", 170 &td->expected), 171 GNUNET_JSON_spec_end () 172 }; 173 174 if (GNUNET_OK != 175 GNUNET_JSON_parse (value, 176 ispec, 177 NULL, NULL)) 178 { 179 GNUNET_break_op (0); 180 return GNUNET_SYSERR; 181 } 182 } 183 gtr->details.ok.transfers_length = tds_length; 184 gtr->details.ok.transfers = tds; 185 gth->cb (gth->cb_cls, 186 gtr); 187 gth->cb = NULL; 188 } 189 return GNUNET_OK; 190 } 191 192 193 /** 194 * Function called when we're done processing the 195 * HTTP GET /private/transfers request. 196 * 197 * @param cls the `struct TALER_MERCHANT_GetPrivateTransfersHandle` 198 * @param response_code HTTP response code, 0 on error 199 * @param response response body, NULL if not in JSON 200 */ 201 static void 202 handle_get_transfers_finished (void *cls, 203 long response_code, 204 const void *response) 205 { 206 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth = cls; 207 const json_t *json = response; 208 struct TALER_MERCHANT_GetPrivateTransfersResponse gtr = { 209 .hr.http_status = (unsigned int) response_code, 210 .hr.reply = json 211 }; 212 213 gth->job = NULL; 214 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 215 "Got /private/transfers response with status code %u\n", 216 (unsigned int) response_code); 217 switch (response_code) 218 { 219 case MHD_HTTP_OK: 220 { 221 const json_t *transfers; 222 struct GNUNET_JSON_Specification spec[] = { 223 GNUNET_JSON_spec_array_const ("transfers", 224 &transfers), 225 GNUNET_JSON_spec_end () 226 }; 227 228 if (GNUNET_OK != 229 GNUNET_JSON_parse (json, 230 spec, 231 NULL, NULL)) 232 { 233 gtr.hr.http_status = 0; 234 gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 235 break; 236 } 237 if (GNUNET_OK == 238 parse_transfers (transfers, 239 >r, 240 gth)) 241 { 242 TALER_MERCHANT_get_private_transfers_cancel (gth); 243 return; 244 } 245 gtr.hr.http_status = 0; 246 gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 247 break; 248 } 249 case MHD_HTTP_UNAUTHORIZED: 250 gtr.hr.ec = TALER_JSON_get_error_code (json); 251 gtr.hr.hint = TALER_JSON_get_error_hint (json); 252 break; 253 case MHD_HTTP_NOT_FOUND: 254 gtr.hr.ec = TALER_JSON_get_error_code (json); 255 gtr.hr.hint = TALER_JSON_get_error_hint (json); 256 break; 257 default: 258 gtr.hr.ec = TALER_JSON_get_error_code (json); 259 gtr.hr.hint = TALER_JSON_get_error_hint (json); 260 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 261 "Unexpected response code %u/%d\n", 262 (unsigned int) response_code, 263 (int) gtr.hr.ec); 264 break; 265 } 266 gth->cb (gth->cb_cls, 267 >r); 268 TALER_MERCHANT_get_private_transfers_cancel (gth); 269 } 270 271 272 struct TALER_MERCHANT_GetPrivateTransfersHandle * 273 TALER_MERCHANT_get_private_transfers_create ( 274 struct GNUNET_CURL_Context *ctx, 275 const char *url) 276 { 277 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth; 278 279 gth = GNUNET_new (struct TALER_MERCHANT_GetPrivateTransfersHandle); 280 gth->ctx = ctx; 281 gth->base_url = GNUNET_strdup (url); 282 gth->expected = TALER_EXCHANGE_YNA_ALL; 283 gth->before = GNUNET_TIME_UNIT_FOREVER_TS; 284 gth->after = GNUNET_TIME_UNIT_ZERO_TS; 285 gth->offset = UINT64_MAX; 286 return gth; 287 } 288 289 290 enum GNUNET_GenericReturnValue 291 TALER_MERCHANT_get_private_transfers_set_options_ ( 292 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth, 293 unsigned int num_options, 294 const struct TALER_MERCHANT_GetPrivateTransfersOptionValue *options) 295 { 296 for (unsigned int i = 0; i < num_options; i++) 297 { 298 const struct TALER_MERCHANT_GetPrivateTransfersOptionValue *opt = 299 &options[i]; 300 301 switch (opt->option) 302 { 303 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_END: 304 return GNUNET_OK; 305 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_PAYTO_URI: 306 GNUNET_free (gth->payto_uri_enc); 307 gth->payto_uri_enc = GNUNET_strdup ( 308 opt->details.payto_uri.full_payto); 309 break; 310 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_BEFORE: 311 gth->before = opt->details.before; 312 break; 313 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_AFTER: 314 gth->after = opt->details.after; 315 break; 316 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_LIMIT: 317 gth->limit = opt->details.limit; 318 break; 319 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_OFFSET: 320 gth->offset = opt->details.offset; 321 gth->have_offset = true; 322 break; 323 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_EXPECTED: 324 gth->expected = opt->details.expected; 325 break; 326 default: 327 GNUNET_break (0); 328 return GNUNET_NO; 329 } 330 } 331 return GNUNET_OK; 332 } 333 334 335 enum TALER_ErrorCode 336 TALER_MERCHANT_get_private_transfers_start ( 337 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth, 338 TALER_MERCHANT_GetPrivateTransfersCallback cb, 339 TALER_MERCHANT_GET_PRIVATE_TRANSFERS_RESULT_CLOSURE *cb_cls) 340 { 341 CURL *eh; 342 343 gth->cb = cb; 344 gth->cb_cls = cb_cls; 345 { 346 const char *expected_s = NULL; 347 char limit_s[30]; 348 char offset_s[30]; 349 char before_s[30]; 350 char after_s[30]; 351 352 if (TALER_EXCHANGE_YNA_ALL != gth->expected) 353 expected_s = TALER_yna_to_string (gth->expected); 354 GNUNET_snprintf (limit_s, 355 sizeof (limit_s), 356 "%lld", 357 (long long) gth->limit); 358 GNUNET_snprintf (offset_s, 359 sizeof (offset_s), 360 "%llu", 361 (unsigned long long) gth->offset); 362 GNUNET_snprintf (before_s, 363 sizeof (before_s), 364 "%llu", 365 (unsigned long long) GNUNET_TIME_timestamp_to_s ( 366 gth->before)); 367 GNUNET_snprintf (after_s, 368 sizeof (after_s), 369 "%llu", 370 (unsigned long long) GNUNET_TIME_timestamp_to_s ( 371 gth->after)); 372 gth->url = TALER_url_join (gth->base_url, 373 "private/transfers", 374 "payto_uri", 375 gth->payto_uri_enc, 376 "expected", 377 expected_s, 378 "limit", 379 0 != gth->limit 380 ? limit_s 381 : NULL, 382 "offset", 383 (gth->have_offset && 384 (UINT64_MAX != gth->offset)) 385 ? offset_s 386 : NULL, 387 "before", 388 GNUNET_TIME_absolute_is_never ( 389 gth->before.abs_time) 390 ? NULL 391 : before_s, 392 "after", 393 GNUNET_TIME_absolute_is_zero ( 394 gth->after.abs_time) 395 ? NULL 396 : after_s, 397 NULL); 398 } 399 if (NULL == gth->url) 400 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 401 eh = TALER_MERCHANT_curl_easy_get_ (gth->url); 402 if (NULL == eh) 403 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 404 gth->job = GNUNET_CURL_job_add (gth->ctx, 405 eh, 406 &handle_get_transfers_finished, 407 gth); 408 if (NULL == gth->job) 409 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 410 return TALER_EC_NONE; 411 } 412 413 414 void 415 TALER_MERCHANT_get_private_transfers_cancel ( 416 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth) 417 { 418 if (NULL != gth->job) 419 { 420 GNUNET_CURL_job_cancel (gth->job); 421 gth->job = NULL; 422 } 423 GNUNET_free (gth->url); 424 GNUNET_free (gth->base_url); 425 GNUNET_free (gth->payto_uri_enc); 426 GNUNET_free (gth); 427 } 428 429 430 /* end of merchant_api_get-private-transfers-new.c */