Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2016-2023 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_tbr_get_history.c
21 : * @brief library that fakes being a Taler bank for testcases
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_tbr_get_history.h"
35 :
36 :
37 : /**
38 : * Function called to clean up a history context.
39 : *
40 : * @param cls a `struct HistoryContext *`
41 : */
42 : static void
43 0 : history_cleanup (void *cls)
44 : {
45 0 : struct HistoryContext *hc = cls;
46 :
47 0 : json_decref (hc->history);
48 0 : GNUNET_free (hc);
49 0 : }
50 :
51 :
52 : MHD_RESULT
53 0 : TALER_FAKEBANK_tbr_get_history (
54 : struct TALER_FAKEBANK_Handle *h,
55 : struct MHD_Connection *connection,
56 : const char *account,
57 : void **con_cls)
58 : {
59 0 : struct ConnectionContext *cc = *con_cls;
60 : struct HistoryContext *hc;
61 : const struct Transaction *pos;
62 : enum GNUNET_GenericReturnValue ret;
63 : bool in_shutdown;
64 : const char *acc_payto_uri;
65 :
66 0 : if (NULL == cc)
67 : {
68 0 : cc = GNUNET_new (struct ConnectionContext);
69 0 : cc->ctx_cleaner = &history_cleanup;
70 0 : *con_cls = cc;
71 0 : hc = GNUNET_new (struct HistoryContext);
72 0 : cc->ctx = hc;
73 0 : hc->history = json_array ();
74 0 : if (NULL == hc->history)
75 : {
76 0 : GNUNET_break (0);
77 0 : return MHD_NO;
78 : }
79 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
80 : "Handling /accounts/%s/taler-revenue/history request\n",
81 : account);
82 0 : if (GNUNET_OK !=
83 0 : (ret = TALER_FAKEBANK_common_parse_history_args (h,
84 : connection,
85 : &hc->ha)))
86 : {
87 0 : GNUNET_break_op (0);
88 0 : return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
89 : }
90 0 : hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
91 0 : GNUNET_assert (0 ==
92 : pthread_mutex_lock (&h->big_lock));
93 0 : if (UINT64_MAX == hc->ha.start_idx)
94 0 : hc->ha.start_idx = h->serial_counter;
95 0 : hc->acc = TALER_FAKEBANK_lookup_account_ (h,
96 : account,
97 : NULL);
98 0 : if (NULL == hc->acc)
99 : {
100 0 : GNUNET_assert (0 ==
101 : pthread_mutex_unlock (&h->big_lock));
102 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
103 : "Account %s is unknown\n",
104 : account);
105 0 : return TALER_MHD_reply_with_error (connection,
106 : MHD_HTTP_NOT_FOUND,
107 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
108 : account);
109 : }
110 : }
111 : else
112 : {
113 0 : hc = cc->ctx;
114 0 : GNUNET_assert (0 ==
115 : pthread_mutex_lock (&h->big_lock));
116 : }
117 :
118 0 : if (! hc->ha.have_start)
119 : {
120 0 : pos = (0 > hc->ha.delta)
121 0 : ? hc->acc->in_tail
122 0 : : hc->acc->in_head;
123 : }
124 : else
125 : {
126 0 : struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
127 : bool overflow;
128 : uint64_t dir;
129 0 : bool skip = true;
130 :
131 0 : overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
132 0 : dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
133 : /* If account does not match, linear scan for
134 : first matching account. */
135 0 : while ( (! overflow) &&
136 0 : (NULL != t) &&
137 0 : (t->credit_account != hc->acc) )
138 : {
139 0 : skip = false;
140 0 : t = h->transactions[(t->row_id + dir) % h->ram_limit];
141 0 : if ( (NULL != t) &&
142 0 : (t->row_id == hc->ha.start_idx) )
143 0 : overflow = true; /* full circle, give up! */
144 : }
145 0 : if ( (NULL == t) ||
146 : overflow)
147 : {
148 0 : in_shutdown = h->in_shutdown;
149 : /* FIXME: these conditions are unclear to me. */
150 0 : if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
151 0 : (0 < hc->ha.delta))
152 : {
153 0 : acc_payto_uri = hc->acc->payto_uri;
154 0 : GNUNET_assert (0 ==
155 : pthread_mutex_unlock (&h->big_lock));
156 0 : if (overflow)
157 : {
158 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
159 : "Transactions lost due to RAM limits\n");
160 0 : return TALER_MHD_reply_with_ec (
161 : connection,
162 : TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
163 : NULL);
164 : }
165 0 : goto finish;
166 : }
167 0 : if (in_shutdown)
168 : {
169 0 : acc_payto_uri = hc->acc->payto_uri;
170 0 : GNUNET_assert (0 ==
171 : pthread_mutex_unlock (&h->big_lock));
172 0 : goto finish;
173 : }
174 0 : TALER_FAKEBANK_start_lp_ (h,
175 : connection,
176 : hc->acc,
177 : GNUNET_TIME_absolute_get_remaining (
178 : hc->timeout),
179 : LP_CREDIT,
180 : NULL);
181 0 : GNUNET_assert (0 ==
182 : pthread_mutex_unlock (&h->big_lock));
183 0 : return MHD_YES;
184 : }
185 0 : if (skip)
186 : {
187 : /* range from application is exclusive, skip the
188 : matching entry */
189 0 : if (0 > hc->ha.delta)
190 0 : pos = t->prev_in;
191 : else
192 0 : pos = t->next_in;
193 : }
194 : else
195 : {
196 0 : pos = t;
197 : }
198 : }
199 0 : if (NULL != pos)
200 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
201 : "Returning %lld credit transactions starting (inclusive) from %llu\n",
202 : (long long) hc->ha.delta,
203 : (unsigned long long) pos->row_id);
204 0 : while ( (0 != hc->ha.delta) &&
205 : (NULL != pos) )
206 : {
207 : json_t *trans;
208 : char *subject;
209 :
210 0 : if (T_DEBIT != pos->type)
211 : {
212 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
213 : "Unexpected CREDIT transaction #%llu for account `%s'\n",
214 : (unsigned long long) pos->row_id,
215 : account);
216 0 : if (0 > hc->ha.delta)
217 0 : pos = pos->prev_in;
218 0 : if (0 < hc->ha.delta)
219 0 : pos = pos->next_in;
220 0 : continue;
221 : }
222 :
223 : {
224 : char *wtids;
225 :
226 0 : wtids = GNUNET_STRINGS_data_to_string_alloc (
227 0 : &pos->subject.debit.wtid,
228 : sizeof (pos->subject.debit.wtid));
229 0 : GNUNET_asprintf (&subject,
230 : "%s %s",
231 : wtids,
232 0 : pos->subject.debit.exchange_base_url);
233 0 : GNUNET_free (wtids);
234 : }
235 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
236 : "Found transaction over %s with subject %s\n",
237 : TALER_amount2s (&pos->amount),
238 : subject);
239 0 : trans = GNUNET_JSON_PACK (
240 : GNUNET_JSON_pack_string ("type",
241 : "RESERVE"),
242 : GNUNET_JSON_pack_uint64 ("row_id",
243 : pos->row_id),
244 : GNUNET_JSON_pack_timestamp ("date",
245 : pos->date),
246 : TALER_JSON_pack_amount ("amount",
247 : &pos->amount),
248 : GNUNET_JSON_pack_string ("debit_account",
249 : pos->debit_account->payto_uri),
250 : GNUNET_JSON_pack_string ("subject",
251 : subject));
252 0 : GNUNET_free (subject);
253 0 : GNUNET_assert (NULL != trans);
254 0 : GNUNET_assert (0 ==
255 : json_array_append_new (hc->history,
256 : trans));
257 0 : if (hc->ha.delta > 0)
258 0 : hc->ha.delta--;
259 : else
260 0 : hc->ha.delta++;
261 0 : if (0 > hc->ha.delta)
262 0 : pos = pos->prev_in;
263 0 : if (0 < hc->ha.delta)
264 0 : pos = pos->next_in;
265 : }
266 0 : if ( (0 == json_array_size (hc->history)) &&
267 0 : (! h->in_shutdown) &&
268 0 : (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
269 0 : (0 < hc->ha.delta))
270 : {
271 0 : TALER_FAKEBANK_start_lp_ (h,
272 : connection,
273 : hc->acc,
274 : GNUNET_TIME_absolute_get_remaining (hc->timeout),
275 : LP_CREDIT,
276 : NULL);
277 0 : GNUNET_assert (0 ==
278 : pthread_mutex_unlock (&h->big_lock));
279 0 : return MHD_YES;
280 : }
281 0 : in_shutdown = h->in_shutdown;
282 0 : acc_payto_uri = hc->acc->payto_uri;
283 0 : GNUNET_assert (0 ==
284 : pthread_mutex_unlock (&h->big_lock));
285 0 : finish:
286 0 : if (0 == json_array_size (hc->history))
287 : {
288 0 : GNUNET_break (in_shutdown ||
289 : (! GNUNET_TIME_absolute_is_future (hc->timeout)));
290 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
291 : "Zero transactions found\n");
292 0 : return TALER_MHD_reply_static (connection,
293 : MHD_HTTP_NO_CONTENT,
294 : NULL,
295 : NULL,
296 : 0);
297 : }
298 : {
299 0 : json_t *jh = hc->history;
300 :
301 0 : hc->history = NULL;
302 0 : return TALER_MHD_REPLY_JSON_PACK (
303 : connection,
304 : MHD_HTTP_OK,
305 : GNUNET_JSON_pack_string (
306 : "credit_account",
307 : acc_payto_uri),
308 : GNUNET_JSON_pack_array_steal (
309 : "incoming_transactions",
310 : jh));
311 : }
312 : }
|