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