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