merchant_api_get-private-transfers.c (12542B)
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 TALER_JSON_spec_web_url ("exchange_url", 152 &td->exchange_url), 153 GNUNET_JSON_spec_uint64 ("transfer_serial_id", 154 &td->transfer_serial_id), 155 GNUNET_JSON_spec_mark_optional ( 156 GNUNET_JSON_spec_timestamp ("execution_time", 157 &td->execution_time), 158 NULL), 159 GNUNET_JSON_spec_mark_optional ( 160 GNUNET_JSON_spec_uint64 ("expected_transfer_serial_id", 161 &td->expected_transfer_serial_id), 162 NULL), 163 GNUNET_JSON_spec_bool ("expected", 164 &td->expected), 165 GNUNET_JSON_spec_end () 166 }; 167 168 if (GNUNET_OK != 169 GNUNET_JSON_parse (value, 170 ispec, 171 NULL, NULL)) 172 { 173 GNUNET_break_op (0); 174 return GNUNET_SYSERR; 175 } 176 } 177 gtr->details.ok.transfers_length = tds_length; 178 gtr->details.ok.transfers = tds; 179 gth->cb (gth->cb_cls, 180 gtr); 181 gth->cb = NULL; 182 } 183 return GNUNET_OK; 184 } 185 186 187 /** 188 * Function called when we're done processing the 189 * HTTP GET /private/transfers request. 190 * 191 * @param cls the `struct TALER_MERCHANT_GetPrivateTransfersHandle` 192 * @param response_code HTTP response code, 0 on error 193 * @param response response body, NULL if not in JSON 194 */ 195 static void 196 handle_get_transfers_finished (void *cls, 197 long response_code, 198 const void *response) 199 { 200 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth = cls; 201 const json_t *json = response; 202 struct TALER_MERCHANT_GetPrivateTransfersResponse gtr = { 203 .hr.http_status = (unsigned int) response_code, 204 .hr.reply = json 205 }; 206 207 gth->job = NULL; 208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 209 "Got /private/transfers response with status code %u\n", 210 (unsigned int) response_code); 211 switch (response_code) 212 { 213 case MHD_HTTP_OK: 214 { 215 const json_t *transfers; 216 struct GNUNET_JSON_Specification spec[] = { 217 GNUNET_JSON_spec_array_const ("transfers", 218 &transfers), 219 GNUNET_JSON_spec_end () 220 }; 221 222 if (GNUNET_OK != 223 GNUNET_JSON_parse (json, 224 spec, 225 NULL, NULL)) 226 { 227 gtr.hr.http_status = 0; 228 gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 229 break; 230 } 231 if (GNUNET_OK == 232 parse_transfers (transfers, 233 >r, 234 gth)) 235 { 236 TALER_MERCHANT_get_private_transfers_cancel (gth); 237 return; 238 } 239 gtr.hr.http_status = 0; 240 gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 241 break; 242 } 243 case MHD_HTTP_UNAUTHORIZED: 244 gtr.hr.ec = TALER_JSON_get_error_code (json); 245 gtr.hr.hint = TALER_JSON_get_error_hint (json); 246 break; 247 case MHD_HTTP_NOT_FOUND: 248 gtr.hr.ec = TALER_JSON_get_error_code (json); 249 gtr.hr.hint = TALER_JSON_get_error_hint (json); 250 break; 251 default: 252 gtr.hr.ec = TALER_JSON_get_error_code (json); 253 gtr.hr.hint = TALER_JSON_get_error_hint (json); 254 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 255 "Unexpected response code %u/%d\n", 256 (unsigned int) response_code, 257 (int) gtr.hr.ec); 258 break; 259 } 260 gth->cb (gth->cb_cls, 261 >r); 262 TALER_MERCHANT_get_private_transfers_cancel (gth); 263 } 264 265 266 struct TALER_MERCHANT_GetPrivateTransfersHandle * 267 TALER_MERCHANT_get_private_transfers_create ( 268 struct GNUNET_CURL_Context *ctx, 269 const char *url) 270 { 271 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth; 272 273 gth = GNUNET_new (struct TALER_MERCHANT_GetPrivateTransfersHandle); 274 gth->ctx = ctx; 275 gth->base_url = GNUNET_strdup (url); 276 gth->expected = TALER_EXCHANGE_YNA_ALL; 277 gth->before = GNUNET_TIME_UNIT_FOREVER_TS; 278 gth->after = GNUNET_TIME_UNIT_ZERO_TS; 279 gth->offset = UINT64_MAX; 280 return gth; 281 } 282 283 284 enum GNUNET_GenericReturnValue 285 TALER_MERCHANT_get_private_transfers_set_options_ ( 286 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth, 287 unsigned int num_options, 288 const struct TALER_MERCHANT_GetPrivateTransfersOptionValue *options) 289 { 290 for (unsigned int i = 0; i < num_options; i++) 291 { 292 const struct TALER_MERCHANT_GetPrivateTransfersOptionValue *opt = 293 &options[i]; 294 295 switch (opt->option) 296 { 297 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_END: 298 return GNUNET_OK; 299 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_PAYTO_URI: 300 GNUNET_free (gth->payto_uri_enc); 301 gth->payto_uri_enc = TALER_urlencode ( 302 opt->details.payto_uri.full_payto); 303 break; 304 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_BEFORE: 305 gth->before = opt->details.before; 306 break; 307 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_AFTER: 308 gth->after = opt->details.after; 309 break; 310 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_LIMIT: 311 gth->limit = opt->details.limit; 312 break; 313 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_OFFSET: 314 gth->offset = opt->details.offset; 315 gth->have_offset = true; 316 break; 317 case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_EXPECTED: 318 gth->expected = opt->details.expected; 319 break; 320 default: 321 GNUNET_break (0); 322 return GNUNET_NO; 323 } 324 } 325 return GNUNET_OK; 326 } 327 328 329 enum TALER_ErrorCode 330 TALER_MERCHANT_get_private_transfers_start ( 331 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth, 332 TALER_MERCHANT_GetPrivateTransfersCallback cb, 333 TALER_MERCHANT_GET_PRIVATE_TRANSFERS_RESULT_CLOSURE *cb_cls) 334 { 335 CURL *eh; 336 337 gth->cb = cb; 338 gth->cb_cls = cb_cls; 339 { 340 const char *expected_s = NULL; 341 char limit_s[30]; 342 char offset_s[30]; 343 char before_s[30]; 344 char after_s[30]; 345 346 if (TALER_EXCHANGE_YNA_ALL != gth->expected) 347 expected_s = TALER_yna_to_string (gth->expected); 348 GNUNET_snprintf (limit_s, 349 sizeof (limit_s), 350 "%lld", 351 (long long) gth->limit); 352 GNUNET_snprintf (offset_s, 353 sizeof (offset_s), 354 "%llu", 355 (unsigned long long) gth->offset); 356 GNUNET_snprintf (before_s, 357 sizeof (before_s), 358 "%llu", 359 (unsigned long long) GNUNET_TIME_timestamp_to_s ( 360 gth->before)); 361 GNUNET_snprintf (after_s, 362 sizeof (after_s), 363 "%llu", 364 (unsigned long long) GNUNET_TIME_timestamp_to_s ( 365 gth->after)); 366 gth->url = TALER_url_join (gth->base_url, 367 "private/transfers", 368 "payto_uri", 369 gth->payto_uri_enc, 370 "expected", 371 expected_s, 372 "limit", 373 0 != gth->limit 374 ? limit_s 375 : NULL, 376 "offset", 377 (gth->have_offset && 378 (UINT64_MAX != gth->offset)) 379 ? offset_s 380 : NULL, 381 "before", 382 GNUNET_TIME_absolute_is_never ( 383 gth->before.abs_time) 384 ? NULL 385 : before_s, 386 "after", 387 GNUNET_TIME_absolute_is_zero ( 388 gth->after.abs_time) 389 ? NULL 390 : after_s, 391 NULL); 392 } 393 if (NULL == gth->url) 394 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 395 eh = TALER_MERCHANT_curl_easy_get_ (gth->url); 396 if (NULL == eh) 397 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 398 gth->job = GNUNET_CURL_job_add (gth->ctx, 399 eh, 400 &handle_get_transfers_finished, 401 gth); 402 if (NULL == gth->job) 403 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 404 return TALER_EC_NONE; 405 } 406 407 408 void 409 TALER_MERCHANT_get_private_transfers_cancel ( 410 struct TALER_MERCHANT_GetPrivateTransfersHandle *gth) 411 { 412 if (NULL != gth->job) 413 { 414 GNUNET_CURL_job_cancel (gth->job); 415 gth->job = NULL; 416 } 417 GNUNET_free (gth->url); 418 GNUNET_free (gth->base_url); 419 GNUNET_free (gth->payto_uri_enc); 420 GNUNET_free (gth); 421 } 422 423 424 /* end of merchant_api_get-private-transfers-new.c */