fakebank_twg_get_transfers.c (7086B)
1 /* 2 This file is part of TALER 3 (C) 2024 Taler Systems SA 4 5 TALER 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 TALER 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 TALER; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file bank-lib/fakebank_twg_get_transfers.c 21 * @brief routines to return account histories for the Taler Wire Gateway API 22 * @author Christian Grothoff <christian@grothoff.org> 23 */ 24 #include "taler/platform.h" 25 #include <pthread.h> 26 #include "taler/taler_fakebank_lib.h" 27 #include "taler/taler_bank_service.h" 28 #include "taler/taler_mhd_lib.h" 29 #include <gnunet/gnunet_mhd_compat.h> 30 #include "fakebank.h" 31 #include "fakebank_common_lookup.h" 32 #include "fakebank_common_lp.h" 33 #include "fakebank_common_parser.h" 34 #include "fakebank_twg_get_transfers.h" 35 36 37 MHD_RESULT 38 TALER_FAKEBANK_twg_get_transfers_ ( 39 struct TALER_FAKEBANK_Handle *h, 40 struct MHD_Connection *connection, 41 const char *account, 42 void **con_cls) 43 { 44 struct Transaction *pos; 45 const char *acc_payto_uri; 46 json_t *history; 47 struct Account *acc; 48 int64_t limit = -20; 49 uint64_t offset; 50 bool have_start; 51 const char *status; 52 53 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 54 "Handling /transfers connection %p\n", 55 connection); 56 57 TALER_MHD_parse_request_snumber (connection, 58 "limit", 59 &limit); 60 if (limit > 0) 61 offset = 0; 62 else 63 offset = UINT64_MAX; 64 TALER_MHD_parse_request_number (connection, 65 "offset", 66 &offset); 67 have_start = ((0 != offset) && (UINT64_MAX != offset)); 68 status = MHD_lookup_connection_value (connection, 69 MHD_GET_ARGUMENT_KIND, 70 "status"); 71 if ( (NULL != status) && 72 (0 != strcasecmp (status, 73 "success")) ) 74 { 75 /* we only have successful transactions */ 76 return TALER_MHD_reply_static (connection, 77 MHD_HTTP_NO_CONTENT, 78 NULL, 79 NULL, 80 0); 81 } 82 83 GNUNET_assert (0 == 84 pthread_mutex_lock (&h->big_lock)); 85 if (UINT64_MAX == offset) 86 offset = h->serial_counter; 87 acc = TALER_FAKEBANK_lookup_account_ (h, 88 account, 89 NULL); 90 if (NULL == acc) 91 { 92 GNUNET_assert (0 == 93 pthread_mutex_unlock (&h->big_lock)); 94 return TALER_MHD_reply_with_error (connection, 95 MHD_HTTP_NOT_FOUND, 96 TALER_EC_BANK_UNKNOWN_ACCOUNT, 97 account); 98 } 99 history = json_array (); 100 if (NULL == history) 101 { 102 GNUNET_break (0); 103 GNUNET_assert (0 == 104 pthread_mutex_unlock (&h->big_lock)); 105 return MHD_NO; 106 } 107 108 if (! have_start) 109 { 110 pos = (0 > limit) 111 ? acc->out_tail 112 : acc->out_head; 113 } 114 else 115 { 116 struct Transaction *t = h->transactions[offset % h->ram_limit]; 117 bool overflow; 118 uint64_t dir; 119 bool skip = true; 120 121 dir = (0 > limit) ? (h->ram_limit - 1) : 1; 122 overflow = (t->row_id != offset); 123 /* If account does not match, linear scan for 124 first matching account. */ 125 while ( (! overflow) && 126 (NULL != t) && 127 (t->debit_account != acc) ) 128 { 129 skip = false; 130 t = h->transactions[(t->row_id + dir) % h->ram_limit]; 131 if ( (NULL != t) && 132 (t->row_id == offset) ) 133 overflow = true; /* full circle, give up! */ 134 } 135 if ( (NULL == t) || 136 (t->debit_account != acc) ) 137 { 138 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 139 "Invalid start specified, transaction %llu not with account %s!\n", 140 (unsigned long long) offset, 141 account); 142 GNUNET_assert (0 == 143 pthread_mutex_unlock (&h->big_lock)); 144 return MHD_NO; 145 } 146 if (skip) 147 { 148 /* range is exclusive, skip the matching entry */ 149 if (0 > limit) 150 pos = t->prev_out; 151 else 152 pos = t->next_out; 153 } 154 else 155 { 156 pos = t; 157 } 158 } 159 if (NULL != pos) 160 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 161 "Returning %lld debit transactions starting (inclusive) from %llu\n", 162 (long long) limit, 163 (unsigned long long) pos->row_id); 164 while ( (0 != limit) && 165 (NULL != pos) ) 166 { 167 json_t *trans; 168 char *credit_payto; 169 170 if (T_DEBIT != pos->type) 171 { 172 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 173 "Unexpected CREDIT transaction #%llu for account `%s'\n", 174 (unsigned long long) pos->row_id, 175 account); 176 if (0 > limit) 177 pos = pos->prev_in; 178 if (0 < limit) 179 pos = pos->next_in; 180 continue; 181 } 182 GNUNET_asprintf (&credit_payto, 183 "payto://x-taler-bank/localhost/%s?receiver-name=%s", 184 pos->credit_account->account_name, 185 pos->credit_account->receiver_name); 186 187 trans = GNUNET_JSON_PACK ( 188 GNUNET_JSON_pack_uint64 ("row_id", 189 pos->row_id), 190 GNUNET_JSON_pack_timestamp ("timestamp", 191 pos->date), 192 TALER_JSON_pack_amount ("amount", 193 &pos->amount), 194 GNUNET_JSON_pack_string ("credit_account", 195 credit_payto), 196 GNUNET_JSON_pack_string ("status", 197 "success")); 198 GNUNET_assert (NULL != trans); 199 GNUNET_free (credit_payto); 200 GNUNET_assert (0 == 201 json_array_append_new (history, 202 trans)); 203 if (limit > 0) 204 limit--; 205 else 206 limit++; 207 if (0 > limit) 208 pos = pos->prev_out; 209 if (0 < limit) 210 pos = pos->next_out; 211 } 212 acc_payto_uri = acc->payto_uri; 213 GNUNET_assert (0 == 214 pthread_mutex_unlock (&h->big_lock)); 215 if (0 == json_array_size (history)) 216 { 217 json_decref (history); 218 return TALER_MHD_reply_static (connection, 219 MHD_HTTP_NO_CONTENT, 220 NULL, 221 NULL, 222 0); 223 } 224 return TALER_MHD_REPLY_JSON_PACK ( 225 connection, 226 MHD_HTTP_OK, 227 GNUNET_JSON_pack_string ( 228 "debit_account", 229 acc_payto_uri), 230 GNUNET_JSON_pack_array_steal ( 231 "transfers", 232 history)); 233 }