Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2016-2022 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.c
21 : * @brief library that fakes being a Taler bank for testcases
22 : * @author Christian Grothoff <christian@grothoff.org>
23 : */
24 : // TODO: support adding WAD transfers
25 :
26 : #include "platform.h"
27 : #include <pthread.h>
28 : #include <poll.h>
29 : #ifdef __linux__
30 : #include <sys/eventfd.h>
31 : #endif
32 : #include "taler_fakebank_lib.h"
33 : #include "taler_bank_service.h"
34 : #include "taler_mhd_lib.h"
35 : #include <gnunet/gnunet_mhd_compat.h>
36 :
37 : /**
38 : * Maximum POST request size (for /admin/add-incoming)
39 : */
40 : #define REQUEST_BUFFER_MAX (4 * 1024)
41 :
42 : /**
43 : * How long are exchange base URLs allowed to be at most?
44 : * Set to a relatively low number as this does contribute
45 : * significantly to our RAM consumption.
46 : */
47 : #define MAX_URL_LEN 64
48 :
49 : /**
50 : * Per account information.
51 : */
52 : struct Account;
53 :
54 :
55 : /**
56 : * Types of long polling activities.
57 : */
58 : enum LongPollType
59 : {
60 : /**
61 : * Transfer TO the exchange.
62 : */
63 : LP_CREDIT,
64 :
65 : /**
66 : * Transfer FROM the exchange.
67 : */
68 : LP_DEBIT,
69 :
70 : /**
71 : * Withdraw operation completion/abort.
72 : */
73 : LP_WITHDRAW
74 :
75 : };
76 :
77 : /**
78 : * Client waiting for activity on this account.
79 : */
80 : struct LongPoller
81 : {
82 :
83 : /**
84 : * Kept in a DLL.
85 : */
86 : struct LongPoller *next;
87 :
88 : /**
89 : * Kept in a DLL.
90 : */
91 : struct LongPoller *prev;
92 :
93 : /**
94 : * Account this long poller is waiting on.
95 : */
96 : struct Account *account;
97 :
98 : /**
99 : * Withdraw operation we are waiting on,
100 : * only if @e type is #LP_WITHDRAW, otherwise NULL.
101 : */
102 : const struct WithdrawalOperation *wo;
103 :
104 : /**
105 : * Entry in the heap for this long poller.
106 : */
107 : struct GNUNET_CONTAINER_HeapNode *hn;
108 :
109 : /**
110 : * Client that is waiting for transactions.
111 : */
112 : struct MHD_Connection *conn;
113 :
114 : /**
115 : * When will this long poller time out?
116 : */
117 : struct GNUNET_TIME_Absolute timeout;
118 :
119 : /**
120 : * What does the @e connection wait for?
121 : */
122 : enum LongPollType type;
123 :
124 : };
125 :
126 :
127 : /**
128 : * Details about a transcation we (as the simulated bank) received.
129 : */
130 : struct Transaction;
131 :
132 :
133 : /**
134 : * Information we keep per withdraw operation.
135 : */
136 : struct WithdrawalOperation
137 : {
138 : /**
139 : * Unique (random) operation ID.
140 : */
141 : struct GNUNET_ShortHashCode wopid;
142 :
143 : /**
144 : * Debited account.
145 : */
146 : struct Account *debit_account;
147 :
148 : /**
149 : * Target exchange account, or NULL if unknown.
150 : */
151 : const struct Account *exchange_account;
152 :
153 : /**
154 : * RowID of the resulting transaction, if any. Otherwise 0.
155 : */
156 : uint64_t row_id;
157 :
158 : /**
159 : * Amount transferred.
160 : */
161 : struct TALER_Amount amount;
162 :
163 : /**
164 : * Public key of the reserve, wire transfer subject.
165 : */
166 : struct TALER_ReservePublicKeyP reserve_pub;
167 :
168 : /**
169 : * When was the transaction made? 0 if not yet.
170 : */
171 : struct GNUNET_TIME_Timestamp timestamp;
172 :
173 : /**
174 : * Was the withdrawal aborted?
175 : */
176 : bool aborted;
177 :
178 : /**
179 : * Did the bank confirm the withdrawal?
180 : */
181 : bool confirmation_done;
182 :
183 : /**
184 : * Is @e reserve_pub initialized?
185 : */
186 : bool selection_done;
187 :
188 : };
189 :
190 :
191 : /**
192 : * Per account information.
193 : */
194 : struct Account
195 : {
196 :
197 : /**
198 : * Inbound transactions for this account in a MDLL.
199 : */
200 : struct Transaction *in_head;
201 :
202 : /**
203 : * Inbound transactions for this account in a MDLL.
204 : */
205 : struct Transaction *in_tail;
206 :
207 : /**
208 : * Outbound transactions for this account in a MDLL.
209 : */
210 : struct Transaction *out_head;
211 :
212 : /**
213 : * Outbound transactions for this account in a MDLL.
214 : */
215 : struct Transaction *out_tail;
216 :
217 : /**
218 : * Kept in a DLL.
219 : */
220 : struct LongPoller *lp_head;
221 :
222 : /**
223 : * Kept in a DLL.
224 : */
225 : struct LongPoller *lp_tail;
226 :
227 : /**
228 : * Account name (string, not payto!)
229 : */
230 : char *account_name;
231 :
232 : /**
233 : * Receiver name for payto:// URIs.
234 : */
235 : char *receiver_name;
236 :
237 : /**
238 : * Payto URI for this account.
239 : */
240 : char *payto_uri;
241 :
242 : /**
243 : * Current account balance.
244 : */
245 : struct TALER_Amount balance;
246 :
247 : /**
248 : * true if the balance is negative.
249 : */
250 : bool is_negative;
251 :
252 : };
253 :
254 :
255 : /**
256 : * Details about a transcation we (as the simulated bank) received.
257 : */
258 : struct Transaction
259 : {
260 : /**
261 : * We store inbound transactions in a MDLL.
262 : */
263 : struct Transaction *next_in;
264 :
265 : /**
266 : * We store inbound transactions in a MDLL.
267 : */
268 : struct Transaction *prev_in;
269 :
270 : /**
271 : * We store outbound transactions in a MDLL.
272 : */
273 : struct Transaction *next_out;
274 :
275 : /**
276 : * We store outbound transactions in a MDLL.
277 : */
278 : struct Transaction *prev_out;
279 :
280 : /**
281 : * Amount to be transferred.
282 : */
283 : struct TALER_Amount amount;
284 :
285 : /**
286 : * Account to debit.
287 : */
288 : struct Account *debit_account;
289 :
290 : /**
291 : * Account to credit.
292 : */
293 : struct Account *credit_account;
294 :
295 : /**
296 : * Random unique identifier for the request.
297 : * Used to detect idempotent requests.
298 : */
299 : struct GNUNET_HashCode request_uid;
300 :
301 : /**
302 : * When did the transaction happen?
303 : */
304 : struct GNUNET_TIME_Timestamp date;
305 :
306 : /**
307 : * Number of this transaction.
308 : */
309 : uint64_t row_id;
310 :
311 : /**
312 : * What does the @e subject contain?
313 : */
314 : enum
315 : {
316 : /**
317 : * Transfer TO the exchange.
318 : */
319 : T_CREDIT,
320 :
321 : /**
322 : * Transfer FROM the exchange.
323 : */
324 : T_DEBIT,
325 :
326 : /**
327 : * Exchange-to-exchange WAD transfer.
328 : */
329 : T_WAD,
330 : } type;
331 :
332 : /**
333 : * Wire transfer subject.
334 : */
335 : union
336 : {
337 :
338 : /**
339 : * Used if @e type is T_DEBIT.
340 : */
341 : struct
342 : {
343 :
344 : /**
345 : * Subject of the transfer.
346 : */
347 : struct TALER_WireTransferIdentifierRawP wtid;
348 :
349 : /**
350 : * Base URL of the exchange.
351 : */
352 : char exchange_base_url[MAX_URL_LEN];
353 :
354 : } debit;
355 :
356 : /**
357 : * Used if @e type is T_CREDIT.
358 : */
359 : struct
360 : {
361 :
362 : /**
363 : * Reserve public key of the credit operation.
364 : */
365 : struct TALER_ReservePublicKeyP reserve_pub;
366 :
367 : } credit;
368 :
369 : /**
370 : * Used if @e type is T_WAD.
371 : */
372 : struct
373 : {
374 :
375 : /**
376 : * Subject of the transfer.
377 : */
378 : struct TALER_WadIdentifierP wad;
379 :
380 : /**
381 : * Base URL of the originating exchange.
382 : */
383 : char origin_base_url[MAX_URL_LEN];
384 :
385 : } wad;
386 :
387 : } subject;
388 :
389 : /**
390 : * Has this transaction not yet been subjected to
391 : * #TALER_FAKEBANK_check_credit() or #TALER_FAKEBANK_check_debit() and
392 : * should thus be counted in #TALER_FAKEBANK_check_empty()?
393 : */
394 : bool unchecked;
395 : };
396 :
397 :
398 : /**
399 : * Handle for the fake bank.
400 : */
401 : struct TALER_FAKEBANK_Handle
402 : {
403 : /**
404 : * We store transactions in a revolving array.
405 : */
406 : struct Transaction **transactions;
407 :
408 : /**
409 : * HTTP server we run to pretend to be the "test" bank.
410 : */
411 : struct MHD_Daemon *mhd_bank;
412 :
413 : /**
414 : * Task running HTTP server for the "test" bank,
415 : * unless we are using a thread pool (then NULL).
416 : */
417 : struct GNUNET_SCHEDULER_Task *mhd_task;
418 :
419 : /**
420 : * Task for expiring long-polling connections,
421 : * unless we are using a thread pool (then NULL).
422 : */
423 : struct GNUNET_SCHEDULER_Task *lp_task;
424 :
425 : /**
426 : * Task for expiring long-polling connections, unless we are using the
427 : * GNUnet scheduler (then NULL).
428 : */
429 : pthread_t lp_thread;
430 :
431 : /**
432 : * MIN-heap of long pollers, sorted by timeout.
433 : */
434 : struct GNUNET_CONTAINER_Heap *lp_heap;
435 :
436 : /**
437 : * Hashmap of reserve public keys to
438 : * `struct Transaction` with that reserve public
439 : * key. Used to prevent public-key re-use.
440 : */
441 : struct GNUNET_CONTAINER_MultiPeerMap *rpubs;
442 :
443 : /**
444 : * Hashmap of short hashes (wopids) to
445 : * `struct WithdrawalOperation`.
446 : * Used to lookup withdrawal operations.
447 : */
448 : struct GNUNET_CONTAINER_MultiShortmap *wops;
449 :
450 : /**
451 : * (Base) URL to suggest for the exchange. Can
452 : * be NULL if there is no suggestion to be made.
453 : */
454 : char *exchange_url;
455 :
456 : /**
457 : * Lock for accessing @a rpubs map.
458 : */
459 : pthread_mutex_t rpubs_lock;
460 :
461 : /**
462 : * Hashmap of hashes of account names to `struct Account`.
463 : */
464 : struct GNUNET_CONTAINER_MultiHashMap *accounts;
465 :
466 : /**
467 : * Lock for accessing @a accounts hash map.
468 : */
469 : pthread_mutex_t accounts_lock;
470 :
471 : /**
472 : * Hashmap of hashes of transaction request_uids to `struct Transaction`.
473 : */
474 : struct GNUNET_CONTAINER_MultiHashMap *uuid_map;
475 :
476 : /**
477 : * Lock for accessing @a uuid_map.
478 : */
479 : pthread_mutex_t uuid_map_lock;
480 :
481 : /**
482 : * Lock for accessing the internals of
483 : * accounts and transaction array entries.
484 : */
485 : pthread_mutex_t big_lock;
486 :
487 : /**
488 : * Current transaction counter.
489 : */
490 : uint64_t serial_counter;
491 :
492 : /**
493 : * Number of transactions we keep in memory (at most).
494 : */
495 : uint64_t ram_limit;
496 :
497 : /**
498 : * Currency used by the fakebank.
499 : */
500 : char *currency;
501 :
502 : /**
503 : * Hostname of the fakebank.
504 : */
505 : char *hostname;
506 :
507 : /**
508 : * BaseURL of the fakebank.
509 : */
510 : char *my_baseurl;
511 :
512 : /**
513 : * Our port number.
514 : */
515 : uint16_t port;
516 :
517 : #ifdef __linux__
518 : /**
519 : * Event FD to signal @a lp_thread a change in
520 : * @a lp_heap.
521 : */
522 : int lp_event;
523 : #else
524 : /**
525 : * Pipe input to signal @a lp_thread a change in
526 : * @a lp_heap.
527 : */
528 : int lp_event_in;
529 :
530 : /**
531 : * Pipe output to signal @a lp_thread a change in
532 : * @a lp_heap.
533 : */
534 : int lp_event_out;
535 : #endif
536 :
537 : /**
538 : * Set to true once we are shutting down.
539 : */
540 : bool in_shutdown;
541 :
542 : /**
543 : * Should we run MHD immediately again?
544 : */
545 : bool mhd_again;
546 :
547 : #if EPOLL_SUPPORT
548 : /**
549 : * Boxed @e mhd_fd.
550 : */
551 : struct GNUNET_NETWORK_Handle *mhd_rfd;
552 :
553 : /**
554 : * File descriptor to use to wait for MHD.
555 : */
556 : int mhd_fd;
557 : #endif
558 : };
559 :
560 :
561 : /**
562 : * Special address "con_cls" can point to to indicate that the handler has
563 : * been called more than once already (was previously suspended).
564 : */
565 : static int special_ptr;
566 :
567 :
568 : /**
569 : * Task run whenever HTTP server operations are pending.
570 : *
571 : * @param cls the `struct TALER_FAKEBANK_Handle`
572 : */
573 : static void
574 : run_mhd (void *cls);
575 :
576 :
577 : /**
578 : * Find withdrawal operation @a wopid in @a h.
579 : *
580 : * @param h fakebank handle
581 : * @param wopid withdrawal operation ID as a string
582 : * @return NULL if operation was not found
583 : */
584 : static struct WithdrawalOperation *
585 0 : lookup_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
586 : const char *wopid)
587 : {
588 : struct GNUNET_ShortHashCode sh;
589 :
590 0 : if (NULL == h->wops)
591 0 : return NULL;
592 0 : if (GNUNET_OK !=
593 0 : GNUNET_STRINGS_string_to_data (wopid,
594 : strlen (wopid),
595 : &sh,
596 : sizeof (sh)))
597 : {
598 0 : GNUNET_break_op (0);
599 0 : return NULL;
600 : }
601 0 : return GNUNET_CONTAINER_multishortmap_get (h->wops,
602 : &sh);
603 : }
604 :
605 :
606 : /**
607 : * Trigger the @a lp. Frees associated resources,
608 : * except the entry of @a lp in the timeout heap.
609 : * Must be called while the ``big lock`` is held.
610 : *
611 : * @param[in] lp long poller to trigger
612 : * @param[in,out] h fakebank handle
613 : */
614 : static void
615 0 : lp_trigger (struct LongPoller *lp,
616 : struct TALER_FAKEBANK_Handle *h)
617 : {
618 0 : struct Account *acc = lp->account;
619 :
620 0 : GNUNET_CONTAINER_DLL_remove (acc->lp_head,
621 : acc->lp_tail,
622 : lp);
623 0 : MHD_resume_connection (lp->conn);
624 0 : GNUNET_free (lp);
625 0 : h->mhd_again = true;
626 : #ifdef __linux__
627 0 : if (-1 == h->lp_event)
628 : #else
629 : if ( (-1 == h->lp_event_in) &&
630 : (-1 == h->lp_event_out) )
631 : #endif
632 : {
633 0 : if (NULL != h->mhd_task)
634 0 : GNUNET_SCHEDULER_cancel (h->mhd_task);
635 0 : h->mhd_task =
636 0 : GNUNET_SCHEDULER_add_now (&run_mhd,
637 : h);
638 : }
639 0 : }
640 :
641 :
642 : /**
643 : * Thread that is run to wake up connections that have hit their timeout. Runs
644 : * until in_shutdown is set to true. Must be send signals via lp_event on
645 : * shutdown and/or whenever the heap changes to an earlier timeout.
646 : *
647 : * @param cls a `struct TALER_FAKEBANK_Handle *`
648 : * @return NULL
649 : */
650 : static void *
651 2 : lp_expiration_thread (void *cls)
652 : {
653 2 : struct TALER_FAKEBANK_Handle *h = cls;
654 :
655 2 : GNUNET_assert (0 ==
656 : pthread_mutex_lock (&h->big_lock));
657 4 : while (! h->in_shutdown)
658 : {
659 : struct LongPoller *lp;
660 : int timeout_ms;
661 :
662 2 : lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
663 2 : while ( (NULL != lp) &&
664 0 : GNUNET_TIME_absolute_is_past (lp->timeout))
665 : {
666 0 : GNUNET_assert (lp ==
667 : GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
668 0 : lp_trigger (lp,
669 : h);
670 0 : lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
671 : }
672 2 : if (NULL != lp)
673 : {
674 : struct GNUNET_TIME_Relative rem;
675 : unsigned long long left_ms;
676 :
677 0 : rem = GNUNET_TIME_absolute_get_remaining (lp->timeout);
678 0 : left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
679 0 : if (left_ms > INT_MAX)
680 0 : timeout_ms = INT_MAX;
681 : else
682 0 : timeout_ms = (int) left_ms;
683 : }
684 : else
685 : {
686 2 : timeout_ms = -1; /* infinity */
687 : }
688 2 : GNUNET_assert (0 ==
689 : pthread_mutex_unlock (&h->big_lock));
690 : {
691 2 : struct pollfd p = {
692 : #ifdef __linux__
693 2 : .fd = h->lp_event,
694 : #else
695 : .fd = h->lp_event_out,
696 : #endif
697 : .events = POLLIN
698 : };
699 : int ret;
700 :
701 2 : ret = poll (&p,
702 : 1,
703 : timeout_ms);
704 2 : if (-1 == ret)
705 : {
706 0 : if (EINTR != errno)
707 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
708 : "poll");
709 : }
710 2 : else if (1 == ret)
711 : {
712 : /* clear event */
713 : uint64_t ev;
714 : ssize_t iret;
715 :
716 : #ifdef __linux__
717 2 : iret = read (h->lp_event,
718 : #else
719 : iret = read (h->lp_event_out,
720 : #endif
721 : &ev,
722 : sizeof (ev));
723 2 : if (-1 == iret)
724 : {
725 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
726 : "read");
727 : }
728 : else
729 : {
730 2 : GNUNET_break (sizeof (uint64_t) == iret);
731 : }
732 : }
733 : }
734 2 : GNUNET_assert (0 ==
735 : pthread_mutex_lock (&h->big_lock));
736 : }
737 2 : GNUNET_assert (0 ==
738 : pthread_mutex_unlock (&h->big_lock));
739 2 : return NULL;
740 : }
741 :
742 :
743 : /**
744 : * Lookup account with @a name, and if it does not exist, create it.
745 : *
746 : * @param[in,out] h bank to lookup account at
747 : * @param name account name to resolve
748 : * @param receiver_name receiver name in payto:// URI,
749 : * NULL if the account must already exist
750 : * @return account handle, NULL if account does not yet exist
751 : */
752 : static struct Account *
753 22 : lookup_account (struct TALER_FAKEBANK_Handle *h,
754 : const char *name,
755 : const char *receiver_name)
756 : {
757 : struct GNUNET_HashCode hc;
758 : size_t slen;
759 : struct Account *account;
760 :
761 22 : memset (&hc,
762 : 0,
763 : sizeof (hc));
764 22 : slen = strlen (name);
765 22 : GNUNET_CRYPTO_hash (name,
766 : slen,
767 : &hc);
768 22 : GNUNET_assert (0 ==
769 : pthread_mutex_lock (&h->accounts_lock));
770 22 : account = GNUNET_CONTAINER_multihashmap_get (h->accounts,
771 : &hc);
772 22 : if (NULL == account)
773 : {
774 8 : if (NULL == receiver_name)
775 : {
776 1 : GNUNET_assert (0 ==
777 : pthread_mutex_unlock (&h->accounts_lock));
778 1 : return NULL;
779 : }
780 7 : account = GNUNET_new (struct Account);
781 7 : account->account_name = GNUNET_strdup (name);
782 7 : account->receiver_name = GNUNET_strdup (receiver_name);
783 7 : GNUNET_asprintf (&account->payto_uri,
784 : "payto://x-taler-bank/%s/%s?receiver-name=%s",
785 : h->hostname,
786 : account->account_name,
787 : account->receiver_name);
788 7 : GNUNET_assert (GNUNET_OK ==
789 : TALER_amount_set_zero (h->currency,
790 : &account->balance));
791 7 : GNUNET_assert (GNUNET_OK ==
792 : GNUNET_CONTAINER_multihashmap_put (h->accounts,
793 : &hc,
794 : account,
795 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
796 : }
797 21 : GNUNET_assert (0 ==
798 : pthread_mutex_unlock (&h->accounts_lock));
799 21 : return account;
800 : }
801 :
802 :
803 : /**
804 : * Generate log messages for failed check operation.
805 : *
806 : * @param h handle to output transaction log for
807 : */
808 : static void
809 0 : check_log (struct TALER_FAKEBANK_Handle *h)
810 : {
811 0 : for (uint64_t i = 0; i<h->ram_limit; i++)
812 : {
813 0 : struct Transaction *t = h->transactions[i];
814 :
815 0 : if (NULL == t)
816 0 : continue;
817 0 : if (! t->unchecked)
818 0 : continue;
819 0 : switch (t->type)
820 : {
821 0 : case T_DEBIT:
822 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
823 : "%s -> %s (%s) %s (%s)\n",
824 : t->debit_account->account_name,
825 : t->credit_account->account_name,
826 : TALER_amount2s (&t->amount),
827 : t->subject.debit.exchange_base_url,
828 : "DEBIT");
829 0 : break;
830 0 : case T_CREDIT:
831 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
832 : "%s -> %s (%s) %s (%s)\n",
833 : t->debit_account->account_name,
834 : t->credit_account->account_name,
835 : TALER_amount2s (&t->amount),
836 : TALER_B2S (&t->subject.credit.reserve_pub),
837 : "CREDIT");
838 0 : break;
839 0 : case T_WAD:
840 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
841 : "%s -> %s (%s) %s[%s] (%s)\n",
842 : t->debit_account->account_name,
843 : t->credit_account->account_name,
844 : TALER_amount2s (&t->amount),
845 : t->subject.wad.origin_base_url,
846 : TALER_B2S (&t->subject.wad),
847 : "WAD");
848 0 : break;
849 : }
850 0 : }
851 0 : }
852 :
853 :
854 : enum GNUNET_GenericReturnValue
855 0 : TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
856 : const struct TALER_Amount *want_amount,
857 : const char *want_debit,
858 : const char *want_credit,
859 : const char *exchange_base_url,
860 : struct TALER_WireTransferIdentifierRawP *wtid)
861 : {
862 : struct Account *debit_account;
863 : struct Account *credit_account;
864 :
865 0 : GNUNET_assert (0 ==
866 : strcasecmp (want_amount->currency,
867 : h->currency));
868 0 : debit_account = lookup_account (h,
869 : want_debit,
870 : NULL);
871 0 : credit_account = lookup_account (h,
872 : want_credit,
873 : NULL);
874 0 : if (NULL == debit_account)
875 : {
876 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
877 : "I wanted: %s->%s (%s) from exchange %s (DEBIT), but debit account does not even exist!\n",
878 : want_debit,
879 : want_credit,
880 : TALER_amount2s (want_amount),
881 : exchange_base_url);
882 0 : return GNUNET_SYSERR;
883 : }
884 0 : if (NULL == credit_account)
885 : {
886 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
887 : "I wanted: %s->%s (%s) from exchange %s (DEBIT), but credit account does not even exist!\n",
888 : want_debit,
889 : want_credit,
890 : TALER_amount2s (want_amount),
891 : exchange_base_url);
892 0 : return GNUNET_SYSERR;
893 : }
894 0 : for (struct Transaction *t = debit_account->out_tail;
895 : NULL != t;
896 0 : t = t->prev_out)
897 : {
898 0 : if ( (t->unchecked) &&
899 0 : (credit_account == t->credit_account) &&
900 0 : (T_DEBIT == t->type) &&
901 0 : (0 == TALER_amount_cmp (want_amount,
902 0 : &t->amount)) &&
903 0 : (0 == strcasecmp (exchange_base_url,
904 0 : t->subject.debit.exchange_base_url)) )
905 : {
906 0 : *wtid = t->subject.debit.wtid;
907 0 : t->unchecked = false;
908 0 : return GNUNET_OK;
909 : }
910 : }
911 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
912 : "Did not find matching transaction! I have:\n");
913 0 : check_log (h);
914 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
915 : "I wanted: %s->%s (%s) from exchange %s (DEBIT)\n",
916 : want_debit,
917 : want_credit,
918 : TALER_amount2s (want_amount),
919 : exchange_base_url);
920 0 : return GNUNET_SYSERR;
921 : }
922 :
923 :
924 : enum GNUNET_GenericReturnValue
925 0 : TALER_FAKEBANK_check_credit (struct TALER_FAKEBANK_Handle *h,
926 : const struct TALER_Amount *want_amount,
927 : const char *want_debit,
928 : const char *want_credit,
929 : const struct TALER_ReservePublicKeyP *reserve_pub)
930 : {
931 : struct Account *debit_account;
932 : struct Account *credit_account;
933 :
934 0 : GNUNET_assert (0 == strcasecmp (want_amount->currency,
935 : h->currency));
936 0 : debit_account = lookup_account (h,
937 : want_debit,
938 : NULL);
939 0 : credit_account = lookup_account (h,
940 : want_credit,
941 : NULL);
942 0 : if (NULL == debit_account)
943 : {
944 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
945 : "I wanted:\n%s -> %s (%s) with subject %s (CREDIT) but debit account is unknown.\n",
946 : want_debit,
947 : want_credit,
948 : TALER_amount2s (want_amount),
949 : TALER_B2S (reserve_pub));
950 0 : return GNUNET_SYSERR;
951 : }
952 0 : if (NULL == credit_account)
953 : {
954 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
955 : "I wanted:\n%s -> %s (%s) with subject %s (CREDIT) but credit account is unknown.\n",
956 : want_debit,
957 : want_credit,
958 : TALER_amount2s (want_amount),
959 : TALER_B2S (reserve_pub));
960 0 : return GNUNET_SYSERR;
961 : }
962 0 : for (struct Transaction *t = credit_account->in_tail;
963 : NULL != t;
964 0 : t = t->prev_in)
965 : {
966 0 : if ( (t->unchecked) &&
967 0 : (debit_account == t->debit_account) &&
968 0 : (T_CREDIT == t->type) &&
969 0 : (0 == TALER_amount_cmp (want_amount,
970 0 : &t->amount)) &&
971 0 : (0 == GNUNET_memcmp (reserve_pub,
972 : &t->subject.credit.reserve_pub)) )
973 : {
974 0 : t->unchecked = false;
975 0 : return GNUNET_OK;
976 : }
977 : }
978 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
979 : "Did not find matching transaction!\nI have:\n");
980 0 : check_log (h);
981 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
982 : "I wanted:\n%s -> %s (%s) with subject %s (CREDIT)\n",
983 : want_debit,
984 : want_credit,
985 : TALER_amount2s (want_amount),
986 : TALER_B2S (reserve_pub));
987 0 : return GNUNET_SYSERR;
988 : }
989 :
990 :
991 : /**
992 : * Update @a account balance by @a amount.
993 : *
994 : * The @a big_lock must already be locked when calling
995 : * this function.
996 : *
997 : * @param[in,out] account account to update
998 : * @param amount balance change
999 : * @param debit true to subtract, false to add @a amount
1000 : */
1001 : static void
1002 14 : update_balance (struct Account *account,
1003 : const struct TALER_Amount *amount,
1004 : bool debit)
1005 : {
1006 14 : if (debit == account->is_negative)
1007 : {
1008 8 : GNUNET_assert (0 <=
1009 : TALER_amount_add (&account->balance,
1010 : &account->balance,
1011 : amount));
1012 8 : return;
1013 : }
1014 6 : if (0 <= TALER_amount_cmp (&account->balance,
1015 : amount))
1016 : {
1017 3 : GNUNET_assert (0 <=
1018 : TALER_amount_subtract (&account->balance,
1019 : &account->balance,
1020 : amount));
1021 : }
1022 : else
1023 : {
1024 3 : GNUNET_assert (0 <=
1025 : TALER_amount_subtract (&account->balance,
1026 : amount,
1027 : &account->balance));
1028 3 : account->is_negative = ! account->is_negative;
1029 : }
1030 : }
1031 :
1032 :
1033 : /**
1034 : * Add transaction to the debit and credit accounts,
1035 : * updating the balances as needed.
1036 : *
1037 : * The transaction @a t must already be locked
1038 : * when calling this function!
1039 : *
1040 : * @param[in,out] h bank handle
1041 : * @param[in,out] t transaction to add to account lists
1042 : */
1043 : static void
1044 7 : post_transaction (struct TALER_FAKEBANK_Handle *h,
1045 : struct Transaction *t)
1046 : {
1047 7 : struct Account *debit_acc = t->debit_account;
1048 7 : struct Account *credit_acc = t->credit_account;
1049 : uint64_t row_id;
1050 : struct Transaction *old;
1051 :
1052 7 : GNUNET_assert (0 ==
1053 : pthread_mutex_lock (&h->big_lock));
1054 7 : row_id = ++h->serial_counter;
1055 7 : old = h->transactions[row_id % h->ram_limit];
1056 7 : h->transactions[row_id % h->ram_limit] = t;
1057 7 : t->row_id = row_id;
1058 7 : GNUNET_CONTAINER_MDLL_insert_tail (out,
1059 : debit_acc->out_head,
1060 : debit_acc->out_tail,
1061 : t);
1062 7 : update_balance (debit_acc,
1063 7 : &t->amount,
1064 : true);
1065 7 : GNUNET_CONTAINER_MDLL_insert_tail (in,
1066 : credit_acc->in_head,
1067 : credit_acc->in_tail,
1068 : t);
1069 7 : update_balance (credit_acc,
1070 7 : &t->amount,
1071 : false);
1072 7 : if (NULL != old)
1073 : {
1074 : struct Account *da;
1075 : struct Account *ca;
1076 :
1077 0 : da = old->debit_account;
1078 0 : ca = old->credit_account;
1079 : /* slot was already in use, must clean out old
1080 : entry first! */
1081 0 : GNUNET_CONTAINER_MDLL_remove (out,
1082 : da->out_head,
1083 : da->out_tail,
1084 : old);
1085 0 : GNUNET_CONTAINER_MDLL_remove (in,
1086 : ca->in_head,
1087 : ca->in_tail,
1088 : old);
1089 : }
1090 7 : GNUNET_assert (0 ==
1091 : pthread_mutex_unlock (&h->big_lock));
1092 7 : if ( (NULL != old) &&
1093 0 : (T_DEBIT == old->type) )
1094 : {
1095 0 : GNUNET_assert (0 ==
1096 : pthread_mutex_lock (&h->uuid_map_lock));
1097 0 : GNUNET_assert (GNUNET_OK ==
1098 : GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
1099 : &old->request_uid,
1100 : old));
1101 0 : GNUNET_assert (0 ==
1102 : pthread_mutex_unlock (&h->uuid_map_lock));
1103 : }
1104 7 : GNUNET_free (old);
1105 7 : }
1106 :
1107 :
1108 : /**
1109 : * Trigger long pollers that might have been waiting
1110 : * for @a t.
1111 : *
1112 : * @param h fakebank handle
1113 : * @param t transaction to notify on
1114 : */
1115 : static void
1116 7 : notify_transaction (struct TALER_FAKEBANK_Handle *h,
1117 : struct Transaction *t)
1118 : {
1119 7 : struct Account *debit_acc = t->debit_account;
1120 7 : struct Account *credit_acc = t->credit_account;
1121 : struct LongPoller *nxt;
1122 :
1123 7 : GNUNET_assert (0 ==
1124 : pthread_mutex_lock (&h->big_lock));
1125 7 : for (struct LongPoller *lp = debit_acc->lp_head;
1126 : NULL != lp;
1127 0 : lp = nxt)
1128 : {
1129 0 : nxt = lp->next;
1130 0 : if (LP_DEBIT == lp->type)
1131 : {
1132 0 : GNUNET_assert (lp ==
1133 : GNUNET_CONTAINER_heap_remove_node (lp->hn));
1134 0 : lp_trigger (lp,
1135 : h);
1136 : }
1137 : }
1138 7 : for (struct LongPoller *lp = credit_acc->lp_head;
1139 : NULL != lp;
1140 0 : lp = nxt)
1141 : {
1142 0 : nxt = lp->next;
1143 0 : if (LP_CREDIT == lp->type)
1144 : {
1145 0 : GNUNET_assert (lp ==
1146 : GNUNET_CONTAINER_heap_remove_node (lp->hn));
1147 0 : lp_trigger (lp,
1148 : h);
1149 : }
1150 : }
1151 7 : GNUNET_assert (0 ==
1152 : pthread_mutex_unlock (&h->big_lock));
1153 7 : }
1154 :
1155 :
1156 : /**
1157 : * Tell the fakebank to create another wire transfer *from* an exchange.
1158 : *
1159 : * @param h fake bank handle
1160 : * @param debit_account account to debit
1161 : * @param credit_account account to credit
1162 : * @param amount amount to transfer
1163 : * @param subject wire transfer subject to use
1164 : * @param exchange_base_url exchange URL
1165 : * @param request_uid unique number to make the request unique, or NULL to create one
1166 : * @param[out] ret_row_id pointer to store the row ID of this transaction
1167 : * @param[out] timestamp set to the time of the transfer
1168 : * @return #GNUNET_YES if the transfer was successful,
1169 : * #GNUNET_SYSERR if the request_uid was reused for a different transfer
1170 : */
1171 : static enum GNUNET_GenericReturnValue
1172 4 : make_transfer (
1173 : struct TALER_FAKEBANK_Handle *h,
1174 : const char *debit_account,
1175 : const char *credit_account,
1176 : const struct TALER_Amount *amount,
1177 : const struct TALER_WireTransferIdentifierRawP *subject,
1178 : const char *exchange_base_url,
1179 : const struct GNUNET_HashCode *request_uid,
1180 : uint64_t *ret_row_id,
1181 : struct GNUNET_TIME_Timestamp *timestamp)
1182 : {
1183 : struct Transaction *t;
1184 : struct Account *debit_acc;
1185 : struct Account *credit_acc;
1186 : size_t url_len;
1187 :
1188 4 : GNUNET_assert (0 == strcasecmp (amount->currency,
1189 : h->currency));
1190 4 : GNUNET_assert (NULL != debit_account);
1191 4 : GNUNET_assert (NULL != credit_account);
1192 4 : GNUNET_break (0 != strncasecmp ("payto://",
1193 : debit_account,
1194 : strlen ("payto://")));
1195 4 : GNUNET_break (0 != strncasecmp ("payto://",
1196 : credit_account,
1197 : strlen ("payto://")));
1198 4 : url_len = strlen (exchange_base_url);
1199 4 : GNUNET_assert (url_len < MAX_URL_LEN);
1200 4 : debit_acc = lookup_account (h,
1201 : debit_account,
1202 : debit_account);
1203 4 : credit_acc = lookup_account (h,
1204 : credit_account,
1205 : credit_account);
1206 4 : if (NULL != request_uid)
1207 : {
1208 4 : GNUNET_assert (0 ==
1209 : pthread_mutex_lock (&h->uuid_map_lock));
1210 4 : t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
1211 : request_uid);
1212 4 : if (NULL != t)
1213 : {
1214 0 : if ( (debit_acc != t->debit_account) ||
1215 0 : (credit_acc != t->credit_account) ||
1216 0 : (0 != TALER_amount_cmp (amount,
1217 0 : &t->amount)) ||
1218 0 : (T_DEBIT != t->type) ||
1219 0 : (0 != GNUNET_memcmp (subject,
1220 : &t->subject.debit.wtid)) )
1221 : {
1222 : /* Transaction exists, but with different details. */
1223 0 : GNUNET_break (0);
1224 0 : GNUNET_assert (0 ==
1225 : pthread_mutex_unlock (&h->uuid_map_lock));
1226 0 : return GNUNET_SYSERR;
1227 : }
1228 0 : *ret_row_id = t->row_id;
1229 0 : *timestamp = t->date;
1230 0 : GNUNET_assert (0 ==
1231 : pthread_mutex_unlock (&h->uuid_map_lock));
1232 0 : return GNUNET_OK;
1233 : }
1234 4 : GNUNET_assert (0 ==
1235 : pthread_mutex_unlock (&h->uuid_map_lock));
1236 : }
1237 4 : t = GNUNET_new (struct Transaction);
1238 4 : t->unchecked = true;
1239 4 : t->debit_account = debit_acc;
1240 4 : t->credit_account = credit_acc;
1241 4 : t->amount = *amount;
1242 4 : t->date = GNUNET_TIME_timestamp_get ();
1243 4 : if (NULL != timestamp)
1244 4 : *timestamp = t->date;
1245 4 : t->type = T_DEBIT;
1246 4 : memcpy (t->subject.debit.exchange_base_url,
1247 : exchange_base_url,
1248 : url_len);
1249 4 : t->subject.debit.wtid = *subject;
1250 4 : if (NULL == request_uid)
1251 0 : GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
1252 : &t->request_uid);
1253 : else
1254 4 : t->request_uid = *request_uid;
1255 4 : post_transaction (h,
1256 : t);
1257 4 : GNUNET_assert (0 ==
1258 : pthread_mutex_lock (&h->uuid_map_lock));
1259 4 : GNUNET_assert (GNUNET_OK ==
1260 : GNUNET_CONTAINER_multihashmap_put (
1261 : h->uuid_map,
1262 : &t->request_uid,
1263 : t,
1264 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1265 4 : GNUNET_assert (0 ==
1266 : pthread_mutex_unlock (&h->uuid_map_lock));
1267 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1268 : "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
1269 : (unsigned long long) t->row_id,
1270 : debit_account,
1271 : credit_account,
1272 : TALER_amount2s (amount),
1273 : TALER_B2S (subject),
1274 : exchange_base_url);
1275 4 : *ret_row_id = t->row_id;
1276 4 : notify_transaction (h,
1277 : t);
1278 4 : return GNUNET_OK;
1279 : }
1280 :
1281 :
1282 : /**
1283 : * Tell the fakebank to create another wire transfer *to* an exchange.
1284 : *
1285 : * @param h fake bank handle
1286 : * @param debit_account account to debit
1287 : * @param credit_account account to credit
1288 : * @param amount amount to transfer
1289 : * @param reserve_pub reserve public key to use in subject
1290 : * @param[out] row_id serial_id of the transfer
1291 : * @param[out] timestamp when was the transfer made
1292 : * @return #GNUNET_OK on success
1293 : */
1294 : static enum GNUNET_GenericReturnValue
1295 4 : make_admin_transfer (
1296 : struct TALER_FAKEBANK_Handle *h,
1297 : const char *debit_account,
1298 : const char *credit_account,
1299 : const struct TALER_Amount *amount,
1300 : const struct TALER_ReservePublicKeyP *reserve_pub,
1301 : uint64_t *row_id,
1302 : struct GNUNET_TIME_Timestamp *timestamp)
1303 : {
1304 : struct Transaction *t;
1305 : const struct GNUNET_PeerIdentity *pid;
1306 : struct Account *debit_acc;
1307 : struct Account *credit_acc;
1308 :
1309 : GNUNET_static_assert (sizeof (*pid) ==
1310 : sizeof (*reserve_pub));
1311 4 : pid = (const struct GNUNET_PeerIdentity *) reserve_pub;
1312 4 : GNUNET_assert (NULL != debit_account);
1313 4 : GNUNET_assert (NULL != credit_account);
1314 4 : GNUNET_assert (0 == strcasecmp (amount->currency,
1315 : h->currency));
1316 4 : GNUNET_break (0 != strncasecmp ("payto://",
1317 : debit_account,
1318 : strlen ("payto://")));
1319 4 : GNUNET_break (0 != strncasecmp ("payto://",
1320 : credit_account,
1321 : strlen ("payto://")));
1322 4 : debit_acc = lookup_account (h,
1323 : debit_account,
1324 : debit_account);
1325 4 : credit_acc = lookup_account (h,
1326 : credit_account,
1327 : credit_account);
1328 4 : GNUNET_assert (0 ==
1329 : pthread_mutex_lock (&h->rpubs_lock));
1330 4 : t = GNUNET_CONTAINER_multipeermap_get (h->rpubs,
1331 : pid);
1332 4 : GNUNET_assert (0 ==
1333 : pthread_mutex_unlock (&h->rpubs_lock));
1334 4 : if (NULL != t)
1335 : {
1336 : /* duplicate reserve public key not allowed */
1337 1 : GNUNET_break_op (0);
1338 1 : return GNUNET_NO;
1339 : }
1340 :
1341 3 : t = GNUNET_new (struct Transaction);
1342 3 : t->unchecked = true;
1343 3 : t->debit_account = debit_acc;
1344 3 : t->credit_account = credit_acc;
1345 3 : t->amount = *amount;
1346 3 : t->date = GNUNET_TIME_timestamp_get ();
1347 3 : if (NULL != timestamp)
1348 3 : *timestamp = t->date;
1349 3 : t->type = T_CREDIT;
1350 3 : t->subject.credit.reserve_pub = *reserve_pub;
1351 3 : post_transaction (h,
1352 : t);
1353 3 : if (NULL != row_id)
1354 3 : *row_id = t->row_id;
1355 3 : GNUNET_assert (0 ==
1356 : pthread_mutex_lock (&h->rpubs_lock));
1357 3 : GNUNET_assert (GNUNET_OK ==
1358 : GNUNET_CONTAINER_multipeermap_put (
1359 : h->rpubs,
1360 : pid,
1361 : t,
1362 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1363 3 : GNUNET_assert (0 ==
1364 : pthread_mutex_unlock (&h->rpubs_lock));
1365 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1366 : "Making transfer from %s to %s over %s and subject %s at row %llu\n",
1367 : debit_account,
1368 : credit_account,
1369 : TALER_amount2s (amount),
1370 : TALER_B2S (reserve_pub),
1371 : (unsigned long long) t->row_id);
1372 3 : notify_transaction (h,
1373 : t);
1374 3 : return GNUNET_OK;
1375 : }
1376 :
1377 :
1378 : enum GNUNET_GenericReturnValue
1379 0 : TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
1380 : {
1381 0 : for (uint64_t i = 0; i<h->ram_limit; i++)
1382 : {
1383 0 : struct Transaction *t = h->transactions[i];
1384 :
1385 0 : if ( (NULL != t) &&
1386 0 : (t->unchecked) )
1387 : {
1388 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1389 : "Expected empty transaction set, but I have:\n");
1390 0 : check_log (h);
1391 0 : return GNUNET_SYSERR;
1392 : }
1393 : }
1394 0 : return GNUNET_OK;
1395 : }
1396 :
1397 :
1398 : /**
1399 : * Helper function to free memory when finished.
1400 : *
1401 : * @param cls NULL
1402 : * @param key key of the account to free (ignored)
1403 : * @param val a `struct Account` to free.
1404 : */
1405 : static enum GNUNET_GenericReturnValue
1406 7 : free_account (void *cls,
1407 : const struct GNUNET_HashCode *key,
1408 : void *val)
1409 : {
1410 7 : struct Account *account = val;
1411 :
1412 : (void) cls;
1413 : (void) key;
1414 7 : GNUNET_assert (NULL == account->lp_head);
1415 7 : GNUNET_free (account->account_name);
1416 7 : GNUNET_free (account->receiver_name);
1417 7 : GNUNET_free (account->payto_uri);
1418 7 : GNUNET_free (account);
1419 7 : return GNUNET_OK;
1420 : }
1421 :
1422 :
1423 : /**
1424 : * Helper function to free memory when finished.
1425 : *
1426 : * @param cls NULL
1427 : * @param key key of the operation to free (ignored)
1428 : * @param val a `struct WithdrawalOperation *` to free.
1429 : */
1430 : static enum GNUNET_GenericReturnValue
1431 0 : free_withdraw_op (void *cls,
1432 : const struct GNUNET_ShortHashCode *key,
1433 : void *val)
1434 : {
1435 0 : struct WithdrawalOperation *wo = val;
1436 :
1437 : (void) cls;
1438 : (void) key;
1439 0 : GNUNET_free (wo);
1440 0 : return GNUNET_OK;
1441 : }
1442 :
1443 :
1444 : void
1445 3 : TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
1446 : {
1447 3 : if (NULL != h->lp_task)
1448 : {
1449 0 : GNUNET_SCHEDULER_cancel (h->lp_task);
1450 0 : h->lp_task = NULL;
1451 : }
1452 : #if EPOLL_SUPPORT
1453 3 : if (NULL != h->mhd_rfd)
1454 : {
1455 1 : GNUNET_NETWORK_socket_free_memory_only_ (h->mhd_rfd);
1456 1 : h->mhd_rfd = NULL;
1457 : }
1458 : #endif
1459 : #ifdef __linux__
1460 3 : if (-1 != h->lp_event)
1461 : #else
1462 : if (-1 != h->lp_event_in && -1 != h->lp_event_out)
1463 : #endif
1464 : {
1465 2 : uint64_t val = 1;
1466 : void *ret;
1467 : struct LongPoller *lp;
1468 :
1469 2 : GNUNET_assert (0 ==
1470 : pthread_mutex_lock (&h->big_lock));
1471 2 : h->in_shutdown = true;
1472 2 : while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
1473 0 : lp_trigger (lp,
1474 : h);
1475 2 : GNUNET_break (sizeof (val) ==
1476 : #ifdef __linux__
1477 : write (h->lp_event,
1478 : #else
1479 : write (h->lp_event_in,
1480 : #endif
1481 : &val,
1482 : sizeof (val)));
1483 2 : GNUNET_assert (0 ==
1484 : pthread_mutex_unlock (&h->big_lock));
1485 2 : GNUNET_break (0 ==
1486 : pthread_join (h->lp_thread,
1487 : &ret));
1488 2 : GNUNET_break (NULL == ret);
1489 : #ifdef __linux__
1490 2 : GNUNET_break (0 == close (h->lp_event));
1491 2 : h->lp_event = -1;
1492 : #else
1493 : GNUNET_break (0 == close (h->lp_event_in));
1494 : GNUNET_break (0 == close (h->lp_event_out));
1495 : h->lp_event_in = -1;
1496 : h->lp_event_out = -1;
1497 : #endif
1498 : }
1499 : else
1500 : {
1501 : struct LongPoller *lp;
1502 :
1503 1 : while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
1504 0 : lp_trigger (lp,
1505 : h);
1506 : }
1507 3 : if (NULL != h->mhd_bank)
1508 : {
1509 3 : MHD_stop_daemon (h->mhd_bank);
1510 3 : h->mhd_bank = NULL;
1511 : }
1512 3 : if (NULL != h->mhd_task)
1513 : {
1514 1 : GNUNET_SCHEDULER_cancel (h->mhd_task);
1515 1 : h->mhd_task = NULL;
1516 : }
1517 3 : if (NULL != h->accounts)
1518 : {
1519 3 : GNUNET_CONTAINER_multihashmap_iterate (h->accounts,
1520 : &free_account,
1521 : NULL);
1522 3 : GNUNET_CONTAINER_multihashmap_destroy (h->accounts);
1523 : }
1524 3 : if (NULL != h->wops)
1525 : {
1526 0 : GNUNET_CONTAINER_multishortmap_iterate (h->wops,
1527 : &free_withdraw_op,
1528 : NULL);
1529 0 : GNUNET_CONTAINER_multishortmap_destroy (h->wops);
1530 : }
1531 3 : GNUNET_CONTAINER_multihashmap_destroy (h->uuid_map);
1532 3 : GNUNET_CONTAINER_multipeermap_destroy (h->rpubs);
1533 3 : GNUNET_CONTAINER_heap_destroy (h->lp_heap);
1534 3 : GNUNET_assert (0 ==
1535 : pthread_mutex_destroy (&h->big_lock));
1536 3 : GNUNET_assert (0 ==
1537 : pthread_mutex_destroy (&h->uuid_map_lock));
1538 3 : GNUNET_assert (0 ==
1539 : pthread_mutex_destroy (&h->accounts_lock));
1540 3 : GNUNET_assert (0 ==
1541 : pthread_mutex_destroy (&h->rpubs_lock));
1542 131107 : for (uint64_t i = 0; i<h->ram_limit; i++)
1543 131104 : GNUNET_free (h->transactions[i]);
1544 3 : GNUNET_free (h->transactions);
1545 3 : GNUNET_free (h->my_baseurl);
1546 3 : GNUNET_free (h->currency);
1547 3 : GNUNET_free (h->exchange_url);
1548 3 : GNUNET_free (h->hostname);
1549 3 : GNUNET_free (h);
1550 3 : }
1551 :
1552 :
1553 : /**
1554 : * Function called whenever MHD is done with a request. If the
1555 : * request was a POST, we may have stored a `struct Buffer *` in the
1556 : * @a con_cls that might still need to be cleaned up. Call the
1557 : * respective function to free the memory.
1558 : *
1559 : * @param cls client-defined closure
1560 : * @param connection connection handle
1561 : * @param con_cls value as set by the last call to
1562 : * the #MHD_AccessHandlerCallback
1563 : * @param toe reason for request termination
1564 : * @see #MHD_OPTION_NOTIFY_COMPLETED
1565 : * @ingroup request
1566 : */
1567 : static void
1568 15 : handle_mhd_completion_callback (void *cls,
1569 : struct MHD_Connection *connection,
1570 : void **con_cls,
1571 : enum MHD_RequestTerminationCode toe)
1572 : {
1573 : /* struct TALER_FAKEBANK_Handle *h = cls; */
1574 : (void) cls;
1575 : (void) connection;
1576 : (void) toe;
1577 15 : if (NULL == *con_cls)
1578 12 : return;
1579 3 : if (&special_ptr == *con_cls)
1580 3 : return;
1581 0 : GNUNET_JSON_post_parser_cleanup (*con_cls);
1582 0 : *con_cls = NULL;
1583 : }
1584 :
1585 :
1586 : /**
1587 : * Handle incoming HTTP request for /admin/add/incoming.
1588 : *
1589 : * @param h the fakebank handle
1590 : * @param connection the connection
1591 : * @param account account into which to deposit the funds (credit)
1592 : * @param upload_data request data
1593 : * @param upload_data_size size of @a upload_data in bytes
1594 : * @param con_cls closure for request (a `struct Buffer *`)
1595 : * @return MHD result code
1596 : */
1597 : static MHD_RESULT
1598 12 : handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
1599 : struct MHD_Connection *connection,
1600 : const char *account,
1601 : const char *upload_data,
1602 : size_t *upload_data_size,
1603 : void **con_cls)
1604 : {
1605 : enum GNUNET_JSON_PostResult pr;
1606 : json_t *json;
1607 : uint64_t row_id;
1608 : struct GNUNET_TIME_Timestamp timestamp;
1609 :
1610 12 : pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
1611 : connection,
1612 : con_cls,
1613 : upload_data,
1614 : upload_data_size,
1615 : &json);
1616 12 : switch (pr)
1617 : {
1618 0 : case GNUNET_JSON_PR_OUT_OF_MEMORY:
1619 0 : GNUNET_break (0);
1620 0 : return MHD_NO;
1621 8 : case GNUNET_JSON_PR_CONTINUE:
1622 8 : return MHD_YES;
1623 0 : case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
1624 0 : GNUNET_break (0);
1625 0 : return MHD_NO;
1626 0 : case GNUNET_JSON_PR_JSON_INVALID:
1627 0 : GNUNET_break (0);
1628 0 : return MHD_NO;
1629 4 : case GNUNET_JSON_PR_SUCCESS:
1630 4 : break;
1631 : }
1632 4 : {
1633 : const char *debit_account;
1634 : struct TALER_Amount amount;
1635 : struct TALER_ReservePublicKeyP reserve_pub;
1636 : char *debit;
1637 : enum GNUNET_GenericReturnValue ret;
1638 : struct GNUNET_JSON_Specification spec[] = {
1639 4 : GNUNET_JSON_spec_fixed_auto ("reserve_pub",
1640 : &reserve_pub),
1641 4 : GNUNET_JSON_spec_string ("debit_account",
1642 : &debit_account),
1643 4 : TALER_JSON_spec_amount ("amount",
1644 4 : h->currency,
1645 : &amount),
1646 4 : GNUNET_JSON_spec_end ()
1647 : };
1648 :
1649 4 : if (GNUNET_OK !=
1650 4 : (ret = TALER_MHD_parse_json_data (connection,
1651 : json,
1652 : spec)))
1653 : {
1654 0 : GNUNET_break_op (0);
1655 0 : json_decref (json);
1656 1 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
1657 : }
1658 4 : if (0 != strcasecmp (amount.currency,
1659 4 : h->currency))
1660 : {
1661 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1662 : "Currency `%s' does not match our configuration\n",
1663 : amount.currency);
1664 0 : json_decref (json);
1665 0 : return TALER_MHD_reply_with_error (
1666 : connection,
1667 : MHD_HTTP_CONFLICT,
1668 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
1669 : NULL);
1670 : }
1671 4 : debit = TALER_xtalerbank_account_from_payto (debit_account);
1672 4 : if (NULL == debit)
1673 : {
1674 0 : GNUNET_break_op (0);
1675 0 : return TALER_MHD_reply_with_error (
1676 : connection,
1677 : MHD_HTTP_BAD_REQUEST,
1678 : TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
1679 : debit_account);
1680 : }
1681 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1682 : "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s\n",
1683 : debit,
1684 : account,
1685 : TALER_B2S (&reserve_pub),
1686 : TALER_amount2s (&amount));
1687 4 : ret = make_admin_transfer (h,
1688 : debit,
1689 : account,
1690 : &amount,
1691 : &reserve_pub,
1692 : &row_id,
1693 : ×tamp);
1694 4 : GNUNET_free (debit);
1695 4 : if (GNUNET_OK != ret)
1696 : {
1697 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1698 : "Reserve public key not unique\n");
1699 1 : json_decref (json);
1700 1 : return TALER_MHD_reply_with_error (
1701 : connection,
1702 : MHD_HTTP_CONFLICT,
1703 : TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
1704 : NULL);
1705 : }
1706 : }
1707 3 : json_decref (json);
1708 :
1709 : /* Finally build response object */
1710 3 : return TALER_MHD_REPLY_JSON_PACK (connection,
1711 : MHD_HTTP_OK,
1712 : GNUNET_JSON_pack_uint64 ("row_id",
1713 : row_id),
1714 : GNUNET_JSON_pack_timestamp ("timestamp",
1715 : timestamp));
1716 : }
1717 :
1718 :
1719 : /**
1720 : * Handle incoming HTTP request for /transfer.
1721 : *
1722 : * @param h the fakebank handle
1723 : * @param connection the connection
1724 : * @param account account making the transfer
1725 : * @param upload_data request data
1726 : * @param upload_data_size size of @a upload_data in bytes
1727 : * @param con_cls closure for request (a `struct Buffer *`)
1728 : * @return MHD result code
1729 : */
1730 : static MHD_RESULT
1731 12 : handle_transfer (struct TALER_FAKEBANK_Handle *h,
1732 : struct MHD_Connection *connection,
1733 : const char *account,
1734 : const char *upload_data,
1735 : size_t *upload_data_size,
1736 : void **con_cls)
1737 : {
1738 : enum GNUNET_JSON_PostResult pr;
1739 : json_t *json;
1740 : uint64_t row_id;
1741 : struct GNUNET_TIME_Timestamp ts;
1742 :
1743 12 : pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
1744 : connection,
1745 : con_cls,
1746 : upload_data,
1747 : upload_data_size,
1748 : &json);
1749 12 : switch (pr)
1750 : {
1751 0 : case GNUNET_JSON_PR_OUT_OF_MEMORY:
1752 0 : GNUNET_break (0);
1753 0 : return MHD_NO;
1754 8 : case GNUNET_JSON_PR_CONTINUE:
1755 8 : return MHD_YES;
1756 0 : case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
1757 0 : GNUNET_break (0);
1758 0 : return MHD_NO;
1759 0 : case GNUNET_JSON_PR_JSON_INVALID:
1760 0 : GNUNET_break (0);
1761 0 : return MHD_NO;
1762 4 : case GNUNET_JSON_PR_SUCCESS:
1763 4 : break;
1764 : }
1765 4 : {
1766 : struct GNUNET_HashCode uuid;
1767 : struct TALER_WireTransferIdentifierRawP wtid;
1768 : const char *credit_account;
1769 : char *credit;
1770 : const char *base_url;
1771 : struct TALER_Amount amount;
1772 : enum GNUNET_GenericReturnValue ret;
1773 : struct GNUNET_JSON_Specification spec[] = {
1774 4 : GNUNET_JSON_spec_fixed_auto ("request_uid",
1775 : &uuid),
1776 4 : TALER_JSON_spec_amount ("amount",
1777 4 : h->currency,
1778 : &amount),
1779 4 : GNUNET_JSON_spec_string ("exchange_base_url",
1780 : &base_url),
1781 4 : GNUNET_JSON_spec_fixed_auto ("wtid",
1782 : &wtid),
1783 4 : GNUNET_JSON_spec_string ("credit_account",
1784 : &credit_account),
1785 4 : GNUNET_JSON_spec_end ()
1786 : };
1787 :
1788 4 : if (GNUNET_OK !=
1789 4 : (ret = TALER_MHD_parse_json_data (connection,
1790 : json,
1791 : spec)))
1792 : {
1793 0 : GNUNET_break_op (0);
1794 0 : json_decref (json);
1795 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
1796 : }
1797 : {
1798 : enum GNUNET_GenericReturnValue ret;
1799 :
1800 4 : credit = TALER_xtalerbank_account_from_payto (credit_account);
1801 4 : if (NULL == credit)
1802 : {
1803 0 : GNUNET_break_op (0);
1804 0 : return TALER_MHD_reply_with_error (
1805 : connection,
1806 : MHD_HTTP_BAD_REQUEST,
1807 : TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
1808 : credit_account);
1809 : }
1810 4 : ret = make_transfer (h,
1811 : account,
1812 : credit,
1813 : &amount,
1814 : &wtid,
1815 : base_url,
1816 : &uuid,
1817 : &row_id,
1818 : &ts);
1819 4 : if (GNUNET_OK != ret)
1820 : {
1821 : MHD_RESULT res;
1822 : char *uids;
1823 :
1824 0 : GNUNET_break (0);
1825 0 : uids = GNUNET_STRINGS_data_to_string_alloc (&uuid,
1826 : sizeof (uuid));
1827 0 : json_decref (json);
1828 0 : res = TALER_MHD_reply_with_error (connection,
1829 : MHD_HTTP_CONFLICT,
1830 : TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED,
1831 : uids);
1832 0 : GNUNET_free (uids);
1833 0 : return res;
1834 : }
1835 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1836 : "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
1837 : account,
1838 : credit,
1839 : TALER_B2S (&wtid),
1840 : TALER_amount2s (&amount),
1841 : base_url);
1842 4 : GNUNET_free (credit);
1843 : }
1844 : }
1845 4 : json_decref (json);
1846 :
1847 : /* Finally build response object */
1848 4 : return TALER_MHD_REPLY_JSON_PACK (
1849 : connection,
1850 : MHD_HTTP_OK,
1851 : GNUNET_JSON_pack_uint64 ("row_id",
1852 : row_id),
1853 : GNUNET_JSON_pack_timestamp ("timestamp",
1854 : ts));
1855 : }
1856 :
1857 :
1858 : /**
1859 : * Handle incoming HTTP request for / (home page).
1860 : *
1861 : * @param h the fakebank handle
1862 : * @param connection the connection
1863 : * @return MHD result code
1864 : */
1865 : static MHD_RESULT
1866 1 : handle_home_page (struct TALER_FAKEBANK_Handle *h,
1867 : struct MHD_Connection *connection)
1868 : {
1869 : MHD_RESULT ret;
1870 : struct MHD_Response *resp;
1871 : #define HELLOMSG "Hello, Fakebank!"
1872 :
1873 : (void) h;
1874 1 : resp = MHD_create_response_from_buffer (
1875 : strlen (HELLOMSG),
1876 : HELLOMSG,
1877 : MHD_RESPMEM_MUST_COPY);
1878 1 : ret = MHD_queue_response (connection,
1879 : MHD_HTTP_OK,
1880 : resp);
1881 1 : MHD_destroy_response (resp);
1882 1 : return ret;
1883 : }
1884 :
1885 :
1886 : /**
1887 : * This is the "base" structure for both the /history and the
1888 : * /history-range API calls.
1889 : */
1890 : struct HistoryArgs
1891 : {
1892 :
1893 : /**
1894 : * Bank account number of the requesting client.
1895 : */
1896 : uint64_t account_number;
1897 :
1898 : /**
1899 : * Index of the starting transaction, exclusive (!).
1900 : */
1901 : uint64_t start_idx;
1902 :
1903 : /**
1904 : * Requested number of results and order
1905 : * (positive: ascending, negative: descending)
1906 : */
1907 : int64_t delta;
1908 :
1909 : /**
1910 : * Timeout for long polling.
1911 : */
1912 : struct GNUNET_TIME_Relative lp_timeout;
1913 :
1914 : /**
1915 : * true if starting point was given.
1916 : */
1917 : bool have_start;
1918 :
1919 : };
1920 :
1921 :
1922 : /**
1923 : * Parse URL history arguments, of _both_ APIs:
1924 : * /history/incoming and /history/outgoing.
1925 : *
1926 : * @param h bank handle to work on
1927 : * @param connection MHD connection.
1928 : * @param[out] ha will contain the parsed values.
1929 : * @return #GNUNET_OK only if the parsing succeeds,
1930 : * #GNUNET_SYSERR if it failed,
1931 : * #GNUNET_NO if it failed and an error was returned
1932 : */
1933 : static enum GNUNET_GenericReturnValue
1934 6 : parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
1935 : struct MHD_Connection *connection,
1936 : struct HistoryArgs *ha)
1937 : {
1938 : const char *start;
1939 : const char *delta;
1940 : const char *long_poll_ms;
1941 : unsigned long long lp_timeout;
1942 : unsigned long long sval;
1943 : long long d;
1944 : char dummy;
1945 :
1946 6 : start = MHD_lookup_connection_value (connection,
1947 : MHD_GET_ARGUMENT_KIND,
1948 : "start");
1949 6 : ha->have_start = (NULL != start);
1950 6 : delta = MHD_lookup_connection_value (connection,
1951 : MHD_GET_ARGUMENT_KIND,
1952 : "delta");
1953 6 : long_poll_ms = MHD_lookup_connection_value (connection,
1954 : MHD_GET_ARGUMENT_KIND,
1955 : "long_poll_ms");
1956 6 : lp_timeout = 0;
1957 6 : if ( (NULL == delta) ||
1958 6 : (1 != sscanf (delta,
1959 : "%lld%c",
1960 : &d,
1961 : &dummy)) )
1962 : {
1963 : /* Fail if one of the above failed. */
1964 : /* Invalid request, given that this is fakebank we impolitely
1965 : * just kill the connection instead of returning a nice error.
1966 : */
1967 0 : GNUNET_break_op (0);
1968 : return (MHD_YES ==
1969 0 : TALER_MHD_reply_with_error (connection,
1970 : MHD_HTTP_BAD_REQUEST,
1971 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1972 : "delta"))
1973 : ? GNUNET_NO
1974 0 : : GNUNET_SYSERR;
1975 : }
1976 6 : if ( (NULL != long_poll_ms) &&
1977 0 : (1 != sscanf (long_poll_ms,
1978 : "%llu%c",
1979 : &lp_timeout,
1980 : &dummy)) )
1981 : {
1982 : /* Fail if one of the above failed. */
1983 : /* Invalid request, given that this is fakebank we impolitely
1984 : * just kill the connection instead of returning a nice error.
1985 : */
1986 0 : GNUNET_break_op (0);
1987 : return (MHD_YES ==
1988 0 : TALER_MHD_reply_with_error (connection,
1989 : MHD_HTTP_BAD_REQUEST,
1990 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1991 : "long_poll_ms"))
1992 : ? GNUNET_NO
1993 0 : : GNUNET_SYSERR;
1994 : }
1995 6 : if ( (NULL != start) &&
1996 0 : (1 != sscanf (start,
1997 : "%llu%c",
1998 : &sval,
1999 : &dummy)) )
2000 : {
2001 : /* Fail if one of the above failed. */
2002 : /* Invalid request, given that this is fakebank we impolitely
2003 : * just kill the connection instead of returning a nice error.
2004 : */
2005 0 : GNUNET_break_op (0);
2006 : return (MHD_YES ==
2007 0 : TALER_MHD_reply_with_error (connection,
2008 : MHD_HTTP_BAD_REQUEST,
2009 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
2010 : "start"))
2011 : ? GNUNET_NO
2012 0 : : GNUNET_SYSERR;
2013 : }
2014 6 : if (NULL == start)
2015 6 : ha->start_idx = (d > 0) ? 0 : h->serial_counter;
2016 : else
2017 0 : ha->start_idx = (uint64_t) sval;
2018 6 : ha->delta = (int64_t) d;
2019 6 : if (0 == ha->delta)
2020 : {
2021 0 : GNUNET_break_op (0);
2022 : return (MHD_YES ==
2023 0 : TALER_MHD_reply_with_error (connection,
2024 : MHD_HTTP_BAD_REQUEST,
2025 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
2026 : "delta"))
2027 : ? GNUNET_NO
2028 0 : : GNUNET_SYSERR;
2029 : }
2030 : ha->lp_timeout
2031 6 : = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
2032 : lp_timeout);
2033 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2034 : "Request for %lld records from %llu\n",
2035 : (long long) ha->delta,
2036 : (unsigned long long) ha->start_idx);
2037 6 : return GNUNET_OK;
2038 : }
2039 :
2040 :
2041 : /**
2042 : * Task run when a long poller is about to time out.
2043 : * Only used in single-threaded mode.
2044 : *
2045 : * @param cls a `struct TALER_FAKEBANK_Handle *`
2046 : */
2047 : static void
2048 0 : lp_timeout (void *cls)
2049 : {
2050 0 : struct TALER_FAKEBANK_Handle *h = cls;
2051 : struct LongPoller *lp;
2052 :
2053 0 : h->lp_task = NULL;
2054 0 : while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
2055 : {
2056 0 : if (GNUNET_TIME_absolute_is_future (lp->timeout))
2057 0 : break;
2058 0 : GNUNET_assert (lp ==
2059 : GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
2060 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2061 : "Timeout reached for long poller %p\n",
2062 : lp->conn);
2063 0 : lp_trigger (lp,
2064 : h);
2065 : }
2066 0 : if (NULL == lp)
2067 0 : return;
2068 0 : h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout,
2069 : &lp_timeout,
2070 : h);
2071 : }
2072 :
2073 :
2074 : /**
2075 : * Reschedule the timeout task of @a h for time @a t.
2076 : *
2077 : * @param h fakebank handle
2078 : * @param t when will the next connection timeout expire
2079 : */
2080 : static void
2081 0 : reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
2082 : struct GNUNET_TIME_Absolute t)
2083 : {
2084 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2085 : "Scheduling timeout task for %s\n",
2086 : GNUNET_STRINGS_absolute_time_to_string (t));
2087 : #ifdef __linux__
2088 0 : if (-1 != h->lp_event)
2089 : #else
2090 : if (-1 != h->lp_event_in && -1 != h->lp_event_out)
2091 : #endif
2092 : {
2093 0 : uint64_t num = 1;
2094 :
2095 0 : GNUNET_break (sizeof (num) ==
2096 : #ifdef __linux__
2097 : write (h->lp_event,
2098 : #else
2099 : write (h->lp_event_in,
2100 : #endif
2101 : &num,
2102 : sizeof (num)));
2103 : }
2104 : else
2105 : {
2106 0 : if (NULL != h->lp_task)
2107 0 : GNUNET_SCHEDULER_cancel (h->lp_task);
2108 0 : h->lp_task = GNUNET_SCHEDULER_add_at (t,
2109 : &lp_timeout,
2110 : h);
2111 : }
2112 0 : }
2113 :
2114 :
2115 : /**
2116 : * Start long-polling for @a connection and @a acc
2117 : * for transfers in @a dir. Must be called with the
2118 : * "big lock" held.
2119 : *
2120 : * @param[in,out] h fakebank handle
2121 : * @param[in,out] connection to suspend
2122 : * @param[in,out] acc account affected
2123 : * @param lp_timeout how long to suspend
2124 : * @param dir direction of transfers to watch for
2125 : * @param wo withdraw operation to watch, only
2126 : * if @a dir is #LP_WITHDRAW
2127 : */
2128 : static void
2129 0 : start_lp (struct TALER_FAKEBANK_Handle *h,
2130 : struct MHD_Connection *connection,
2131 : struct Account *acc,
2132 : struct GNUNET_TIME_Relative lp_timeout,
2133 : enum LongPollType dir,
2134 : const struct WithdrawalOperation *wo)
2135 : {
2136 : struct LongPoller *lp;
2137 : bool toc;
2138 :
2139 0 : lp = GNUNET_new (struct LongPoller);
2140 0 : lp->account = acc;
2141 0 : lp->wo = wo;
2142 0 : lp->conn = connection;
2143 0 : lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
2144 0 : lp->type = dir;
2145 0 : lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
2146 : lp,
2147 : lp->timeout.abs_value_us);
2148 0 : toc = (lp ==
2149 0 : GNUNET_CONTAINER_heap_peek (h->lp_heap));
2150 0 : GNUNET_CONTAINER_DLL_insert (acc->lp_head,
2151 : acc->lp_tail,
2152 : lp);
2153 0 : MHD_suspend_connection (connection);
2154 0 : if (toc)
2155 0 : reschedule_lp_timeout (h,
2156 : lp->timeout);
2157 :
2158 0 : }
2159 :
2160 :
2161 : /**
2162 : * Handle incoming HTTP request for /history/outgoing
2163 : *
2164 : * @param h the fakebank handle
2165 : * @param connection the connection
2166 : * @param account which account the request is about
2167 : * @param con_cls closure for request (NULL or &special_ptr)
2168 : */
2169 : static MHD_RESULT
2170 3 : handle_debit_history (struct TALER_FAKEBANK_Handle *h,
2171 : struct MHD_Connection *connection,
2172 : const char *account,
2173 : void **con_cls)
2174 : {
2175 : struct HistoryArgs ha;
2176 : struct Account *acc;
2177 : struct Transaction *pos;
2178 : json_t *history;
2179 : char *debit_payto;
2180 : enum GNUNET_GenericReturnValue ret;
2181 :
2182 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2183 : "Handling /history/outgoing connection %p\n",
2184 : connection);
2185 3 : if (GNUNET_OK !=
2186 3 : (ret = parse_history_common_args (h,
2187 : connection,
2188 : &ha)))
2189 : {
2190 0 : GNUNET_break_op (0);
2191 0 : return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
2192 : }
2193 3 : if (&special_ptr == *con_cls)
2194 0 : ha.lp_timeout = GNUNET_TIME_UNIT_ZERO;
2195 3 : acc = lookup_account (h,
2196 : account,
2197 : NULL);
2198 3 : if (NULL == acc)
2199 : {
2200 0 : return TALER_MHD_reply_with_error (connection,
2201 : MHD_HTTP_NOT_FOUND,
2202 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
2203 : account);
2204 : }
2205 3 : GNUNET_asprintf (&debit_payto,
2206 : "payto://x-taler-bank/localhost/%s?receiver-name=%s",
2207 : account,
2208 : acc->receiver_name);
2209 3 : history = json_array ();
2210 3 : if (NULL == history)
2211 : {
2212 0 : GNUNET_break (0);
2213 0 : GNUNET_free (debit_payto);
2214 0 : return MHD_NO;
2215 : }
2216 3 : GNUNET_assert (0 ==
2217 : pthread_mutex_lock (&h->big_lock));
2218 3 : if (! ha.have_start)
2219 : {
2220 3 : pos = (0 > ha.delta)
2221 : ? acc->out_tail
2222 3 : : acc->out_head;
2223 : }
2224 : else
2225 : {
2226 0 : struct Transaction *t = h->transactions[ha.start_idx % h->ram_limit];
2227 : bool overflow;
2228 : uint64_t dir;
2229 0 : bool skip = true;
2230 :
2231 0 : dir = (0 > ha.delta) ? (h->ram_limit - 1) : 1;
2232 0 : overflow = (t->row_id != ha.start_idx);
2233 : /* If account does not match, linear scan for
2234 : first matching account. */
2235 0 : while ( (! overflow) &&
2236 0 : (NULL != t) &&
2237 0 : (t->debit_account != acc) )
2238 : {
2239 0 : skip = false;
2240 0 : t = h->transactions[(t->row_id + dir) % h->ram_limit];
2241 0 : if ( (NULL != t) &&
2242 0 : (t->row_id == ha.start_idx) )
2243 0 : overflow = true; /* full circle, give up! */
2244 : }
2245 0 : if ( (NULL == t) ||
2246 : overflow)
2247 : {
2248 0 : GNUNET_free (debit_payto);
2249 0 : if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
2250 0 : (0 < ha.delta))
2251 : {
2252 0 : GNUNET_assert (0 ==
2253 : pthread_mutex_unlock (&h->big_lock));
2254 0 : if (overflow)
2255 0 : return TALER_MHD_reply_with_ec (
2256 : connection,
2257 : TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
2258 : NULL);
2259 0 : return TALER_MHD_REPLY_JSON_PACK (
2260 : connection,
2261 : MHD_HTTP_OK,
2262 : GNUNET_JSON_pack_array_steal (
2263 : "outgoing_transactions",
2264 : history));
2265 : }
2266 0 : *con_cls = &special_ptr;
2267 0 : start_lp (h,
2268 : connection,
2269 : acc,
2270 : ha.lp_timeout,
2271 : LP_DEBIT,
2272 : NULL);
2273 0 : GNUNET_assert (0 ==
2274 : pthread_mutex_unlock (&h->big_lock));
2275 0 : json_decref (history);
2276 0 : return MHD_YES;
2277 : }
2278 0 : if (t->debit_account != acc)
2279 : {
2280 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2281 : "Invalid start specified, transaction %llu not with account %s!\n",
2282 : (unsigned long long) ha.start_idx,
2283 : account);
2284 0 : GNUNET_assert (0 ==
2285 : pthread_mutex_unlock (&h->big_lock));
2286 0 : GNUNET_free (debit_payto);
2287 0 : json_decref (history);
2288 0 : return MHD_NO;
2289 : }
2290 0 : if (skip)
2291 : {
2292 : /* range is exclusive, skip the matching entry */
2293 0 : if (0 > ha.delta)
2294 0 : pos = t->prev_out;
2295 : else
2296 0 : pos = t->next_out;
2297 : }
2298 : else
2299 : {
2300 0 : pos = t;
2301 : }
2302 : }
2303 3 : if (NULL != pos)
2304 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2305 : "Returning %lld debit transactions starting (inclusive) from %llu\n",
2306 : (long long) ha.delta,
2307 : (unsigned long long) pos->row_id);
2308 : else
2309 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2310 : "No debit transactions exist after given starting point\n");
2311 5 : while ( (0 != ha.delta) &&
2312 : (NULL != pos) )
2313 : {
2314 : json_t *trans;
2315 : char *credit_payto;
2316 :
2317 2 : if (T_DEBIT != pos->type)
2318 : {
2319 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2320 : "Unexpected CREDIT transaction #%llu for account `%s'\n",
2321 : (unsigned long long) pos->row_id,
2322 : account);
2323 0 : if (0 > ha.delta)
2324 0 : pos = pos->prev_in;
2325 0 : if (0 < ha.delta)
2326 0 : pos = pos->next_in;
2327 0 : continue;
2328 : }
2329 2 : GNUNET_asprintf (&credit_payto,
2330 : "payto://x-taler-bank/localhost/%s?receiver-name=%s",
2331 2 : pos->credit_account->account_name,
2332 2 : pos->credit_account->receiver_name);
2333 :
2334 2 : trans = GNUNET_JSON_PACK (
2335 : GNUNET_JSON_pack_uint64 ("row_id",
2336 : pos->row_id),
2337 : GNUNET_JSON_pack_timestamp ("date",
2338 : pos->date),
2339 : TALER_JSON_pack_amount ("amount",
2340 : &pos->amount),
2341 : GNUNET_JSON_pack_string ("credit_account",
2342 : credit_payto),
2343 : GNUNET_JSON_pack_string ("debit_account",
2344 : debit_payto), // FIXME #7275: inefficient to return this here always!
2345 : GNUNET_JSON_pack_string ("exchange_base_url",
2346 : pos->subject.debit.exchange_base_url),
2347 : GNUNET_JSON_pack_data_auto ("wtid",
2348 : &pos->subject.debit.wtid));
2349 2 : GNUNET_assert (NULL != trans);
2350 2 : GNUNET_free (credit_payto);
2351 2 : GNUNET_assert (0 ==
2352 : json_array_append_new (history,
2353 : trans));
2354 2 : if (ha.delta > 0)
2355 1 : ha.delta--;
2356 : else
2357 1 : ha.delta++;
2358 2 : if (0 > ha.delta)
2359 1 : pos = pos->prev_out;
2360 2 : if (0 < ha.delta)
2361 1 : pos = pos->next_out;
2362 : }
2363 3 : if ( (0 == json_array_size (history)) &&
2364 1 : (! GNUNET_TIME_relative_is_zero (ha.lp_timeout)) &&
2365 0 : (0 < ha.delta))
2366 : {
2367 0 : *con_cls = &special_ptr;
2368 0 : start_lp (h,
2369 : connection,
2370 : acc,
2371 : ha.lp_timeout,
2372 : LP_DEBIT,
2373 : NULL);
2374 0 : GNUNET_assert (0 ==
2375 : pthread_mutex_unlock (&h->big_lock));
2376 0 : json_decref (history);
2377 0 : return MHD_YES;
2378 : }
2379 3 : GNUNET_assert (0 ==
2380 : pthread_mutex_unlock (&h->big_lock));
2381 3 : GNUNET_free (debit_payto);
2382 3 : return TALER_MHD_REPLY_JSON_PACK (connection,
2383 : MHD_HTTP_OK,
2384 : GNUNET_JSON_pack_array_steal (
2385 : "outgoing_transactions",
2386 : history));
2387 : }
2388 :
2389 :
2390 : /**
2391 : * Handle incoming HTTP request for /history/incoming
2392 : *
2393 : * @param h the fakebank handle
2394 : * @param connection the connection
2395 : * @param account which account the request is about
2396 : * @param con_cls closure for request (NULL or &special_ptr)
2397 : * @return MHD result code
2398 : */
2399 : static MHD_RESULT
2400 3 : handle_credit_history (struct TALER_FAKEBANK_Handle *h,
2401 : struct MHD_Connection *connection,
2402 : const char *account,
2403 : void **con_cls)
2404 : {
2405 : struct HistoryArgs ha;
2406 : struct Account *acc;
2407 : const struct Transaction *pos;
2408 : json_t *history;
2409 : const char *credit_payto;
2410 : enum GNUNET_GenericReturnValue ret;
2411 :
2412 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2413 : "Handling /history/incoming connection %p (%d)\n",
2414 : connection,
2415 : (*con_cls == &special_ptr));
2416 3 : if (GNUNET_OK !=
2417 3 : (ret = parse_history_common_args (h,
2418 : connection,
2419 : &ha)))
2420 : {
2421 0 : GNUNET_break_op (0);
2422 0 : return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
2423 : }
2424 3 : if (&special_ptr == *con_cls)
2425 0 : ha.lp_timeout = GNUNET_TIME_UNIT_ZERO;
2426 3 : *con_cls = &special_ptr;
2427 3 : acc = lookup_account (h,
2428 : account,
2429 : NULL);
2430 3 : if (NULL == acc)
2431 : {
2432 1 : return TALER_MHD_reply_with_error (connection,
2433 : MHD_HTTP_NOT_FOUND,
2434 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
2435 : account);
2436 : }
2437 2 : history = json_array ();
2438 2 : GNUNET_assert (NULL != history);
2439 2 : credit_payto = acc->payto_uri;
2440 2 : GNUNET_assert (0 ==
2441 : pthread_mutex_lock (&h->big_lock));
2442 2 : if (! ha.have_start)
2443 : {
2444 2 : pos = (0 > ha.delta)
2445 : ? acc->in_tail
2446 : : acc->in_head;
2447 : }
2448 : else
2449 : {
2450 0 : struct Transaction *t = h->transactions[ha.start_idx % h->ram_limit];
2451 : bool overflow;
2452 : uint64_t dir;
2453 0 : bool skip = true;
2454 :
2455 0 : overflow = ( (NULL != t) && (t->row_id != ha.start_idx) );
2456 0 : dir = (0 > ha.delta) ? (h->ram_limit - 1) : 1;
2457 : /* If account does not match, linear scan for
2458 : first matching account. */
2459 0 : while ( (! overflow) &&
2460 0 : (NULL != t) &&
2461 0 : (t->credit_account != acc) )
2462 : {
2463 0 : skip = false;
2464 0 : t = h->transactions[(t->row_id + dir) % h->ram_limit];
2465 0 : if ( (NULL != t) &&
2466 0 : (t->row_id == ha.start_idx) )
2467 0 : overflow = true; /* full circle, give up! */
2468 : }
2469 0 : if ( (NULL == t) ||
2470 : overflow)
2471 : {
2472 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2473 : "No transactions available, suspending request\n");
2474 0 : if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
2475 0 : (0 < ha.delta))
2476 : {
2477 0 : GNUNET_assert (0 ==
2478 : pthread_mutex_unlock (&h->big_lock));
2479 0 : if (overflow)
2480 0 : return TALER_MHD_reply_with_ec (
2481 : connection,
2482 : TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
2483 : NULL);
2484 0 : return TALER_MHD_REPLY_JSON_PACK (connection,
2485 : MHD_HTTP_OK,
2486 : GNUNET_JSON_pack_array_steal (
2487 : "incoming_transactions",
2488 : history));
2489 : }
2490 0 : *con_cls = &special_ptr;
2491 0 : start_lp (h,
2492 : connection,
2493 : acc,
2494 : ha.lp_timeout,
2495 : LP_CREDIT,
2496 : NULL);
2497 0 : GNUNET_assert (0 ==
2498 : pthread_mutex_unlock (&h->big_lock));
2499 0 : json_decref (history);
2500 0 : return MHD_YES;
2501 : }
2502 0 : if (skip)
2503 : {
2504 : /* range from application is exclusive, skip the
2505 : matching entry */
2506 0 : if (0 > ha.delta)
2507 0 : pos = t->prev_in;
2508 : else
2509 0 : pos = t->next_in;
2510 : }
2511 : else
2512 : {
2513 0 : pos = t;
2514 : }
2515 : }
2516 2 : if (NULL != pos)
2517 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2518 : "Returning %lld credit transactions starting (inclusive) from %llu\n",
2519 : (long long) ha.delta,
2520 : (unsigned long long) pos->row_id);
2521 : else
2522 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2523 : "No credit transactions exist after given starting point\n");
2524 4 : while ( (0 != ha.delta) &&
2525 : (NULL != pos) )
2526 : {
2527 : json_t *trans;
2528 :
2529 2 : if (T_CREDIT != pos->type)
2530 : {
2531 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2532 : "Unexpected DEBIT transaction #%llu for account `%s'\n",
2533 : (unsigned long long) pos->row_id,
2534 : account);
2535 0 : if (0 > ha.delta)
2536 0 : pos = pos->prev_in;
2537 0 : if (0 < ha.delta)
2538 0 : pos = pos->next_in;
2539 0 : continue;
2540 : }
2541 2 : trans = GNUNET_JSON_PACK (
2542 : GNUNET_JSON_pack_uint64 ("row_id",
2543 : pos->row_id),
2544 : GNUNET_JSON_pack_timestamp ("date",
2545 : pos->date),
2546 : TALER_JSON_pack_amount ("amount",
2547 : &pos->amount),
2548 : GNUNET_JSON_pack_string ("credit_account",
2549 : credit_payto), // FIXME #7275: inefficient to repeat this always here!
2550 : GNUNET_JSON_pack_string ("debit_account",
2551 : pos->debit_account->payto_uri),
2552 : GNUNET_JSON_pack_data_auto ("reserve_pub",
2553 : &pos->subject.credit.reserve_pub));
2554 2 : GNUNET_assert (NULL != trans);
2555 2 : GNUNET_assert (0 ==
2556 : json_array_append_new (history,
2557 : trans));
2558 2 : if (ha.delta > 0)
2559 1 : ha.delta--;
2560 : else
2561 1 : ha.delta++;
2562 2 : if (0 > ha.delta)
2563 1 : pos = pos->prev_in;
2564 2 : if (0 < ha.delta)
2565 1 : pos = pos->next_in;
2566 : }
2567 2 : if ( (0 == json_array_size (history)) &&
2568 0 : (! GNUNET_TIME_relative_is_zero (ha.lp_timeout)) &&
2569 0 : (0 < ha.delta))
2570 : {
2571 0 : *con_cls = &special_ptr;
2572 0 : start_lp (h,
2573 : connection,
2574 : acc,
2575 : ha.lp_timeout,
2576 : LP_CREDIT,
2577 : NULL);
2578 0 : GNUNET_assert (0 ==
2579 : pthread_mutex_unlock (&h->big_lock));
2580 0 : json_decref (history);
2581 0 : return MHD_YES;
2582 : }
2583 2 : GNUNET_assert (0 ==
2584 : pthread_mutex_unlock (&h->big_lock));
2585 2 : return TALER_MHD_REPLY_JSON_PACK (connection,
2586 : MHD_HTTP_OK,
2587 : GNUNET_JSON_pack_array_steal (
2588 : "incoming_transactions",
2589 : history));
2590 : }
2591 :
2592 :
2593 : /**
2594 : * Handle incoming HTTP request.
2595 : *
2596 : * @param h our handle
2597 : * @param connection the connection
2598 : * @param url the requested url
2599 : * @param method the method (POST, GET, ...)
2600 : * @param account which account should process the request
2601 : * @param upload_data request data
2602 : * @param upload_data_size size of @a upload_data in bytes
2603 : * @param con_cls closure
2604 : * @return MHD result code
2605 : */
2606 : static MHD_RESULT
2607 31 : serve (struct TALER_FAKEBANK_Handle *h,
2608 : struct MHD_Connection *connection,
2609 : const char *account,
2610 : const char *url,
2611 : const char *method,
2612 : const char *upload_data,
2613 : size_t *upload_data_size,
2614 : void **con_cls)
2615 : {
2616 31 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2617 : "Fakebank, serving URL `%s' for account `%s'\n",
2618 : url,
2619 : account);
2620 31 : if (0 == strcasecmp (method,
2621 : MHD_HTTP_METHOD_GET))
2622 : {
2623 7 : if ( (0 == strcmp (url,
2624 3 : "/history/incoming")) &&
2625 : (NULL != account) )
2626 3 : return handle_credit_history (h,
2627 : connection,
2628 : account,
2629 : con_cls);
2630 4 : if ( (0 == strcmp (url,
2631 3 : "/history/outgoing")) &&
2632 : (NULL != account) )
2633 3 : return handle_debit_history (h,
2634 : connection,
2635 : account,
2636 : con_cls);
2637 1 : if (0 == strcmp (url,
2638 : "/"))
2639 1 : return handle_home_page (h,
2640 : connection);
2641 : }
2642 24 : else if (0 == strcasecmp (method,
2643 : MHD_HTTP_METHOD_POST))
2644 : {
2645 24 : if ( (0 == strcmp (url,
2646 12 : "/admin/add-incoming")) &&
2647 : (NULL != account) )
2648 12 : return handle_admin_add_incoming (h,
2649 : connection,
2650 : account,
2651 : upload_data,
2652 : upload_data_size,
2653 : con_cls);
2654 12 : if ( (0 == strcmp (url,
2655 12 : "/transfer")) &&
2656 : (NULL != account) )
2657 12 : return handle_transfer (h,
2658 : connection,
2659 : account,
2660 : upload_data,
2661 : upload_data_size,
2662 : con_cls);
2663 : }
2664 : /* Unexpected URL path, just close the connection. */
2665 0 : TALER_LOG_ERROR ("Breaking URL: %s %s\n",
2666 : method,
2667 : url);
2668 0 : GNUNET_break_op (0);
2669 0 : return TALER_MHD_reply_with_error (
2670 : connection,
2671 : MHD_HTTP_NOT_FOUND,
2672 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
2673 : url);
2674 : }
2675 :
2676 :
2677 : /**
2678 : * Handle GET /withdrawal-operation/{wopid} request.
2679 : *
2680 : * @param h the handle
2681 : * @param connection the connection
2682 : * @param wopid the withdrawal operation identifier
2683 : * @param lp how long is the long-polling timeout
2684 : * @param con_cls closure for request
2685 : * @return MHD result code
2686 : */
2687 : static MHD_RESULT
2688 0 : get_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
2689 : struct MHD_Connection *connection,
2690 : const char *wopid,
2691 : struct GNUNET_TIME_Relative lp,
2692 : void **con_cls)
2693 : {
2694 : struct WithdrawalOperation *wo;
2695 :
2696 0 : GNUNET_assert (0 ==
2697 : pthread_mutex_lock (&h->big_lock));
2698 0 : wo = lookup_withdrawal_operation (h,
2699 : wopid);
2700 0 : if (NULL == wo)
2701 : {
2702 0 : GNUNET_assert (0 ==
2703 : pthread_mutex_unlock (&h->big_lock));
2704 0 : return TALER_MHD_reply_with_error (connection,
2705 : MHD_HTTP_NOT_FOUND,
2706 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
2707 : wopid);
2708 : }
2709 0 : if ( (NULL != *con_cls) ||
2710 0 : (GNUNET_TIME_relative_is_zero (lp)) ||
2711 0 : wo->confirmation_done ||
2712 0 : wo->aborted)
2713 : {
2714 : json_t *wt;
2715 :
2716 0 : wt = json_array ();
2717 0 : GNUNET_assert (NULL != wt);
2718 0 : GNUNET_assert (0 ==
2719 : json_array_append_new (wt,
2720 : json_string ("x-taler-bank")));
2721 0 : GNUNET_assert (0 ==
2722 : pthread_mutex_unlock (&h->big_lock));
2723 0 : return TALER_MHD_REPLY_JSON_PACK (
2724 : connection,
2725 : MHD_HTTP_OK,
2726 : GNUNET_JSON_pack_bool ("aborted",
2727 : wo->aborted),
2728 : GNUNET_JSON_pack_bool ("selection_done",
2729 : wo->selection_done),
2730 : GNUNET_JSON_pack_bool ("transfer_done",
2731 : wo->confirmation_done),
2732 : GNUNET_JSON_pack_allow_null (
2733 : GNUNET_JSON_pack_string ("suggested_exchange",
2734 : h->exchange_url)),
2735 : TALER_JSON_pack_amount ("amount",
2736 : &wo->amount),
2737 : GNUNET_JSON_pack_array_steal ("wire_types",
2738 : wt));
2739 : }
2740 :
2741 0 : *con_cls = &special_ptr;
2742 0 : start_lp (h,
2743 : connection,
2744 : wo->debit_account,
2745 : lp,
2746 : LP_WITHDRAW,
2747 : wo);
2748 0 : GNUNET_assert (0 ==
2749 : pthread_mutex_unlock (&h->big_lock));
2750 0 : return MHD_YES;
2751 : }
2752 :
2753 :
2754 : /**
2755 : * Handle POST /withdrawal-operation/ request.
2756 : *
2757 : * @param h our handle
2758 : * @param connection the connection
2759 : * @param wopid the withdrawal operation identifier
2760 : * @param reserve_pub public key of the reserve
2761 : * @param exchange_payto_uri payto://-URI of the exchange
2762 : * @return MHD result code
2763 : */
2764 : static MHD_RESULT
2765 0 : do_post_withdrawal (struct TALER_FAKEBANK_Handle *h,
2766 : struct MHD_Connection *connection,
2767 : const char *wopid,
2768 : const struct TALER_ReservePublicKeyP *reserve_pub,
2769 : const char *exchange_payto_uri)
2770 : {
2771 : struct WithdrawalOperation *wo;
2772 : char *credit_name;
2773 : struct Account *credit_account;
2774 :
2775 0 : GNUNET_assert (0 ==
2776 : pthread_mutex_lock (&h->big_lock));
2777 0 : wo = lookup_withdrawal_operation (h,
2778 : wopid);
2779 0 : if (NULL == wo)
2780 : {
2781 0 : GNUNET_assert (0 ==
2782 : pthread_mutex_unlock (&h->big_lock));
2783 0 : return TALER_MHD_reply_with_error (connection,
2784 : MHD_HTTP_NOT_FOUND,
2785 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
2786 : wopid);
2787 : }
2788 0 : if ( (wo->selection_done) &&
2789 0 : (0 != GNUNET_memcmp (&wo->reserve_pub,
2790 : reserve_pub)) )
2791 : {
2792 0 : GNUNET_assert (0 ==
2793 : pthread_mutex_unlock (&h->big_lock));
2794 0 : return TALER_MHD_reply_with_error (connection,
2795 : MHD_HTTP_CONFLICT,
2796 : TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT,
2797 : "reserve public key changed");
2798 : }
2799 : {
2800 : /* check if reserve_pub is already in use */
2801 : const struct GNUNET_PeerIdentity *pid;
2802 :
2803 0 : pid = (const struct GNUNET_PeerIdentity *) &wo->reserve_pub;
2804 0 : if (GNUNET_CONTAINER_multipeermap_contains (h->rpubs,
2805 : pid))
2806 : {
2807 0 : GNUNET_assert (0 ==
2808 : pthread_mutex_unlock (&h->big_lock));
2809 0 : return TALER_MHD_reply_with_error (connection,
2810 : MHD_HTTP_CONFLICT,
2811 : TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
2812 : NULL);
2813 : }
2814 : }
2815 0 : credit_name = TALER_xtalerbank_account_from_payto (exchange_payto_uri);
2816 0 : if (NULL == credit_name)
2817 : {
2818 0 : GNUNET_break_op (0);
2819 0 : GNUNET_assert (0 ==
2820 : pthread_mutex_unlock (&h->big_lock));
2821 0 : return TALER_MHD_reply_with_error (connection,
2822 : MHD_HTTP_BAD_REQUEST,
2823 : TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
2824 : NULL);
2825 : }
2826 0 : credit_account = lookup_account (h,
2827 : credit_name,
2828 : NULL);
2829 0 : GNUNET_free (credit_name);
2830 0 : if ( (NULL != wo->exchange_account) &&
2831 0 : (credit_account != wo->exchange_account) )
2832 : {
2833 0 : GNUNET_assert (0 ==
2834 : pthread_mutex_unlock (&h->big_lock));
2835 0 : return TALER_MHD_reply_with_error (connection,
2836 : MHD_HTTP_CONFLICT,
2837 : TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT,
2838 : "exchange account changed");
2839 : }
2840 0 : wo->exchange_account = credit_account;
2841 0 : if (NULL == wo->exchange_account)
2842 : {
2843 0 : GNUNET_assert (0 ==
2844 : pthread_mutex_unlock (&h->big_lock));
2845 0 : return TALER_MHD_reply_with_error (connection,
2846 : MHD_HTTP_NOT_FOUND,
2847 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
2848 : exchange_payto_uri);
2849 : }
2850 :
2851 0 : wo->reserve_pub = *reserve_pub;
2852 0 : wo->selection_done = true;
2853 0 : GNUNET_assert (0 ==
2854 : pthread_mutex_unlock (&h->big_lock));
2855 0 : return TALER_MHD_REPLY_JSON_PACK (
2856 : connection,
2857 : MHD_HTTP_OK,
2858 : GNUNET_JSON_pack_bool ("transfer_done",
2859 : wo->confirmation_done));
2860 : }
2861 :
2862 :
2863 : /**
2864 : * Handle POST /withdrawal-operation/ request.
2865 : *
2866 : * @param h our fakebank handle
2867 : * @param connection the connection
2868 : * @param wopid the withdrawal operation identifier
2869 : * @param upload_data request data
2870 : * @param upload_data_size size of @a upload_data in bytes
2871 : * @param con_cls closure for request
2872 : * @return MHD result code
2873 : */
2874 : static MHD_RESULT
2875 0 : post_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
2876 : struct MHD_Connection *connection,
2877 : const char *wopid,
2878 : const void *upload_data,
2879 : size_t *upload_data_size,
2880 : void **con_cls)
2881 : {
2882 : enum GNUNET_JSON_PostResult pr;
2883 : json_t *json;
2884 : MHD_RESULT res;
2885 :
2886 0 : pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
2887 : connection,
2888 : con_cls,
2889 : upload_data,
2890 : upload_data_size,
2891 : &json);
2892 0 : switch (pr)
2893 : {
2894 0 : case GNUNET_JSON_PR_OUT_OF_MEMORY:
2895 0 : GNUNET_break (0);
2896 0 : return MHD_NO;
2897 0 : case GNUNET_JSON_PR_CONTINUE:
2898 0 : return MHD_YES;
2899 0 : case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
2900 0 : GNUNET_break (0);
2901 0 : return MHD_NO;
2902 0 : case GNUNET_JSON_PR_JSON_INVALID:
2903 0 : GNUNET_break (0);
2904 0 : return MHD_NO;
2905 0 : case GNUNET_JSON_PR_SUCCESS:
2906 0 : break;
2907 : }
2908 :
2909 0 : {
2910 : struct TALER_ReservePublicKeyP reserve_pub;
2911 : const char *exchange_payto_url;
2912 : enum GNUNET_GenericReturnValue ret;
2913 : struct GNUNET_JSON_Specification spec[] = {
2914 0 : GNUNET_JSON_spec_fixed_auto ("reserve_pub",
2915 : &reserve_pub),
2916 0 : GNUNET_JSON_spec_string ("selected_exchange",
2917 : &exchange_payto_url),
2918 0 : GNUNET_JSON_spec_end ()
2919 : };
2920 :
2921 0 : if (GNUNET_OK !=
2922 0 : (ret = TALER_MHD_parse_json_data (connection,
2923 : json,
2924 : spec)))
2925 : {
2926 0 : GNUNET_break_op (0);
2927 0 : json_decref (json);
2928 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
2929 : }
2930 0 : res = do_post_withdrawal (h,
2931 : connection,
2932 : wopid,
2933 : &reserve_pub,
2934 : exchange_payto_url);
2935 : }
2936 0 : json_decref (json);
2937 0 : return res;
2938 : }
2939 :
2940 :
2941 : /**
2942 : * Handle incoming HTTP request to the bank integration API.
2943 : *
2944 : * @param h our fakebank handle
2945 : * @param connection the connection
2946 : * @param url the requested url
2947 : * @param method the method (POST, GET, ...)
2948 : * @param upload_data request data
2949 : * @param upload_data_size size of @a upload_data in bytes
2950 : * @param con_cls closure for request
2951 : * @return MHD result code
2952 : */
2953 : static MHD_RESULT
2954 0 : handle_bank_integration (struct TALER_FAKEBANK_Handle *h,
2955 : struct MHD_Connection *connection,
2956 : const char *url,
2957 : const char *method,
2958 : const char *upload_data,
2959 : size_t *upload_data_size,
2960 : void **con_cls)
2961 : {
2962 0 : if (0 == strcasecmp (method,
2963 : MHD_HTTP_METHOD_HEAD))
2964 0 : method = MHD_HTTP_METHOD_GET;
2965 0 : if ( (0 == strcmp (url,
2966 0 : "/config")) &&
2967 0 : (0 == strcasecmp (method,
2968 : MHD_HTTP_METHOD_GET)) )
2969 : {
2970 0 : return TALER_MHD_REPLY_JSON_PACK (
2971 : connection,
2972 : MHD_HTTP_OK,
2973 : GNUNET_JSON_pack_string ("version",
2974 : "0:0:0"),
2975 : GNUNET_JSON_pack_string ("currency",
2976 : h->currency),
2977 : GNUNET_JSON_pack_string ("name",
2978 : "taler-bank-integration"));
2979 : }
2980 0 : if ( (0 == strncmp (url,
2981 : "/withdrawal-operation/",
2982 0 : strlen ("/withdrawal-operation/"))) &&
2983 0 : (0 == strcasecmp (method,
2984 : MHD_HTTP_METHOD_GET)) )
2985 : {
2986 0 : const char *wopid = &url[strlen ("/withdrawal-operation/")];
2987 : const char *lp_s
2988 0 : = MHD_lookup_connection_value (connection,
2989 : MHD_GET_ARGUMENT_KIND,
2990 : "long_poll_ms");
2991 0 : struct GNUNET_TIME_Relative lp = GNUNET_TIME_UNIT_ZERO;
2992 :
2993 0 : if (NULL != lp_s)
2994 : {
2995 : unsigned long long d;
2996 : char dummy;
2997 :
2998 0 : if (1 != sscanf (lp_s,
2999 : "%lld%c",
3000 : &d,
3001 : &dummy))
3002 : {
3003 0 : GNUNET_break_op (0);
3004 0 : return TALER_MHD_reply_with_error (connection,
3005 : MHD_HTTP_BAD_REQUEST,
3006 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3007 : "long_poll_ms");
3008 : }
3009 0 : lp = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
3010 : d);
3011 : }
3012 0 : return get_withdrawal_operation (h,
3013 : connection,
3014 : wopid,
3015 : lp,
3016 : con_cls);
3017 :
3018 : }
3019 0 : if ( (0 == strncmp (url,
3020 : "/withdrawal-operation/",
3021 0 : strlen ("/withdrawal-operation/"))) &&
3022 0 : (0 == strcasecmp (method,
3023 : MHD_HTTP_METHOD_POST)) )
3024 : {
3025 0 : const char *wopid = &url[strlen ("/withdrawal-operation/")];
3026 0 : return post_withdrawal_operation (h,
3027 : connection,
3028 : wopid,
3029 : upload_data,
3030 : upload_data_size,
3031 : con_cls);
3032 : }
3033 :
3034 0 : TALER_LOG_ERROR ("Breaking URL: %s %s\n",
3035 : method,
3036 : url);
3037 0 : GNUNET_break_op (0);
3038 0 : return TALER_MHD_reply_with_error (
3039 : connection,
3040 : MHD_HTTP_NOT_FOUND,
3041 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
3042 : url);
3043 : }
3044 :
3045 :
3046 : /**
3047 : * Handle GET /accounts/${account_name} request
3048 : * to the Taler bank access API.
3049 : *
3050 : * @param h the handle
3051 : * @param connection the connection
3052 : * @param account_name name of the account
3053 : * @return MHD result code
3054 : */
3055 : static MHD_RESULT
3056 0 : get_account_access (struct TALER_FAKEBANK_Handle *h,
3057 : struct MHD_Connection *connection,
3058 : const char *account_name)
3059 : {
3060 : struct Account *acc;
3061 :
3062 0 : GNUNET_assert (0 ==
3063 : pthread_mutex_lock (&h->big_lock));
3064 0 : acc = lookup_account (h,
3065 : account_name,
3066 : NULL);
3067 0 : if (NULL == acc)
3068 : {
3069 0 : GNUNET_assert (0 ==
3070 : pthread_mutex_unlock (&h->big_lock));
3071 0 : return TALER_MHD_reply_with_error (connection,
3072 : MHD_HTTP_NOT_FOUND,
3073 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
3074 : account_name);
3075 : }
3076 :
3077 0 : GNUNET_assert (0 ==
3078 : pthread_mutex_unlock (&h->big_lock));
3079 0 : return TALER_MHD_REPLY_JSON_PACK (
3080 : connection,
3081 : MHD_HTTP_OK,
3082 : GNUNET_JSON_pack_string ("paytoUri", /* FIXME: #7300 */
3083 : acc->payto_uri),
3084 : GNUNET_JSON_pack_object_steal (
3085 : "balance",
3086 : GNUNET_JSON_PACK (
3087 : GNUNET_JSON_pack_string ("credit_debit_indicator",
3088 : acc->is_negative
3089 : ? "debit"
3090 : : "credit"),
3091 : TALER_JSON_pack_amount ("amount",
3092 : &acc->balance))));
3093 : }
3094 :
3095 :
3096 : /**
3097 : * Handle GET /accounts/${account_name}/withdrawals/{withdrawal_id} request
3098 : * to the Taler bank access API.
3099 : *
3100 : * @param h the handle
3101 : * @param connection the connection
3102 : * @param account_name name of the account
3103 : * @param withdrawal_id withdrawal ID to return status of
3104 : * @return MHD result code
3105 : */
3106 : static MHD_RESULT
3107 0 : get_account_withdrawals_access (struct TALER_FAKEBANK_Handle *h,
3108 : struct MHD_Connection *connection,
3109 : const char *account_name,
3110 : const char *withdrawal_id)
3111 : {
3112 : struct WithdrawalOperation *wo;
3113 : struct Account *acc;
3114 :
3115 0 : GNUNET_assert (0 ==
3116 : pthread_mutex_lock (&h->big_lock));
3117 0 : wo = lookup_withdrawal_operation (h,
3118 : withdrawal_id);
3119 0 : if (NULL == wo)
3120 : {
3121 0 : GNUNET_assert (0 ==
3122 : pthread_mutex_unlock (&h->big_lock));
3123 0 : return TALER_MHD_reply_with_error (connection,
3124 : MHD_HTTP_NOT_FOUND,
3125 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
3126 : withdrawal_id);
3127 : }
3128 0 : acc = lookup_account (h,
3129 : account_name,
3130 : NULL);
3131 0 : if (NULL == acc)
3132 : {
3133 0 : GNUNET_assert (0 ==
3134 : pthread_mutex_unlock (&h->big_lock));
3135 0 : return TALER_MHD_reply_with_error (connection,
3136 : MHD_HTTP_NOT_FOUND,
3137 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
3138 : account_name);
3139 : }
3140 0 : if (wo->debit_account != acc)
3141 : {
3142 0 : GNUNET_assert (0 ==
3143 : pthread_mutex_unlock (&h->big_lock));
3144 0 : return TALER_MHD_reply_with_error (connection,
3145 : MHD_HTTP_NOT_FOUND,
3146 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
3147 : account_name);
3148 : }
3149 0 : GNUNET_assert (0 ==
3150 : pthread_mutex_unlock (&h->big_lock));
3151 0 : return TALER_MHD_REPLY_JSON_PACK (
3152 : connection,
3153 : MHD_HTTP_OK,
3154 : GNUNET_JSON_pack_bool ("aborted",
3155 : wo->aborted),
3156 : GNUNET_JSON_pack_bool ("selection_done",
3157 : wo->selection_done),
3158 : GNUNET_JSON_pack_bool ("transfer_done",
3159 : wo->confirmation_done),
3160 : GNUNET_JSON_pack_allow_null (
3161 : GNUNET_JSON_pack_string ("selected_exchange_account",
3162 : wo->exchange_account->payto_uri)),
3163 : GNUNET_JSON_pack_allow_null (
3164 : wo->selection_done
3165 : ? GNUNET_JSON_pack_data_auto ("selected_reserve_pub",
3166 : &wo->reserve_pub)
3167 : : GNUNET_JSON_pack_string ("selected_reserve_pub",
3168 : NULL)),
3169 : TALER_JSON_pack_amount ("amount",
3170 : &wo->amount));
3171 : }
3172 :
3173 :
3174 : /**
3175 : * Handle POST /accounts/$account_name/withdrawals request.
3176 : *
3177 : * @param h our fakebank handle
3178 : * @param connection the connection
3179 : * @param account_name name of the account
3180 : * @param amount amont to withdraw
3181 : * @return MHD result code
3182 : */
3183 : static MHD_RESULT
3184 0 : do_post_account_withdrawals_access (struct TALER_FAKEBANK_Handle *h,
3185 : struct MHD_Connection *connection,
3186 : const char *account_name,
3187 : const struct TALER_Amount *amount)
3188 : {
3189 : struct Account *acc;
3190 : struct WithdrawalOperation *wo;
3191 :
3192 0 : GNUNET_assert (0 ==
3193 : pthread_mutex_lock (&h->big_lock));
3194 0 : acc = lookup_account (h,
3195 : account_name,
3196 : NULL);
3197 0 : if (NULL == acc)
3198 : {
3199 0 : GNUNET_assert (0 ==
3200 : pthread_mutex_unlock (&h->big_lock));
3201 0 : return TALER_MHD_reply_with_error (connection,
3202 : MHD_HTTP_NOT_FOUND,
3203 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
3204 : account_name);
3205 : }
3206 0 : wo = GNUNET_new (struct WithdrawalOperation);
3207 0 : wo->debit_account = acc;
3208 0 : wo->amount = *amount;
3209 0 : if (NULL == h->wops)
3210 : {
3211 0 : h->wops = GNUNET_CONTAINER_multishortmap_create (32,
3212 : GNUNET_YES);
3213 : }
3214 : while (1)
3215 : {
3216 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
3217 0 : &wo->wopid,
3218 : sizeof (wo->wopid));
3219 0 : if (GNUNET_OK ==
3220 0 : GNUNET_CONTAINER_multishortmap_put (h->wops,
3221 0 : &wo->wopid,
3222 : wo,
3223 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
3224 0 : break;
3225 : }
3226 : {
3227 : char *wopids;
3228 : char *uri;
3229 : MHD_RESULT res;
3230 :
3231 0 : wopids = GNUNET_STRINGS_data_to_string_alloc (&wo->wopid,
3232 : sizeof (wo->wopid));
3233 0 : GNUNET_asprintf (&uri,
3234 : "taler+http://withdraw/%s:%u/taler-bank-integration/%s",
3235 : h->hostname,
3236 0 : (unsigned int) h->port,
3237 : wopids);
3238 0 : GNUNET_free (wopids);
3239 0 : res = TALER_MHD_REPLY_JSON_PACK (
3240 : connection,
3241 : MHD_HTTP_OK,
3242 : GNUNET_JSON_pack_string ("taler_withdraw_uri",
3243 : uri),
3244 : GNUNET_JSON_pack_data_auto ("withdrawal_id",
3245 : &wo->wopid));
3246 0 : GNUNET_assert (0 ==
3247 : pthread_mutex_unlock (&h->big_lock));
3248 0 : GNUNET_free (uri);
3249 0 : return res;
3250 : }
3251 : }
3252 :
3253 :
3254 : /**
3255 : * Handle POST /accounts/$account_name/withdrawals request.
3256 : *
3257 : * @param h our fakebank handle
3258 : * @param connection the connection
3259 : * @param account_name name of the account
3260 : * @param upload_data request data
3261 : * @param upload_data_size size of @a upload_data in bytes
3262 : * @param con_cls closure for request
3263 : * @return MHD result code
3264 : */
3265 : static MHD_RESULT
3266 0 : post_account_withdrawals_access (struct TALER_FAKEBANK_Handle *h,
3267 : struct MHD_Connection *connection,
3268 : const char *account_name,
3269 : const void *upload_data,
3270 : size_t *upload_data_size,
3271 : void **con_cls)
3272 : {
3273 : enum GNUNET_JSON_PostResult pr;
3274 : json_t *json;
3275 : MHD_RESULT res;
3276 :
3277 0 : pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
3278 : connection,
3279 : con_cls,
3280 : upload_data,
3281 : upload_data_size,
3282 : &json);
3283 0 : switch (pr)
3284 : {
3285 0 : case GNUNET_JSON_PR_OUT_OF_MEMORY:
3286 0 : GNUNET_break (0);
3287 0 : return MHD_NO;
3288 0 : case GNUNET_JSON_PR_CONTINUE:
3289 0 : return MHD_YES;
3290 0 : case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
3291 0 : GNUNET_break (0);
3292 0 : return MHD_NO;
3293 0 : case GNUNET_JSON_PR_JSON_INVALID:
3294 0 : GNUNET_break (0);
3295 0 : return MHD_NO;
3296 0 : case GNUNET_JSON_PR_SUCCESS:
3297 0 : break;
3298 : }
3299 :
3300 0 : {
3301 : struct TALER_Amount amount;
3302 : enum GNUNET_GenericReturnValue ret;
3303 : struct GNUNET_JSON_Specification spec[] = {
3304 0 : TALER_JSON_spec_amount ("amount",
3305 0 : h->currency,
3306 : &amount),
3307 0 : GNUNET_JSON_spec_end ()
3308 : };
3309 :
3310 0 : if (GNUNET_OK !=
3311 0 : (ret = TALER_MHD_parse_json_data (connection,
3312 : json,
3313 : spec)))
3314 : {
3315 0 : GNUNET_break_op (0);
3316 0 : json_decref (json);
3317 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
3318 : }
3319 0 : res = do_post_account_withdrawals_access (h,
3320 : connection,
3321 : account_name,
3322 : &amount);
3323 : }
3324 0 : json_decref (json);
3325 0 : return res;
3326 : }
3327 :
3328 :
3329 : /**
3330 : * Handle POST /testing/register request.
3331 : *
3332 : * @param h our fakebank handle
3333 : * @param connection the connection
3334 : * @param upload_data request data
3335 : * @param upload_data_size size of @a upload_data in bytes
3336 : * @param con_cls closure for request
3337 : * @return MHD result code
3338 : */
3339 : static MHD_RESULT
3340 0 : post_testing_register (struct TALER_FAKEBANK_Handle *h,
3341 : struct MHD_Connection *connection,
3342 : const void *upload_data,
3343 : size_t *upload_data_size,
3344 : void **con_cls)
3345 : {
3346 : enum GNUNET_JSON_PostResult pr;
3347 : json_t *json;
3348 : MHD_RESULT res;
3349 :
3350 0 : pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
3351 : connection,
3352 : con_cls,
3353 : upload_data,
3354 : upload_data_size,
3355 : &json);
3356 0 : switch (pr)
3357 : {
3358 0 : case GNUNET_JSON_PR_OUT_OF_MEMORY:
3359 0 : GNUNET_break (0);
3360 0 : return MHD_NO;
3361 0 : case GNUNET_JSON_PR_CONTINUE:
3362 0 : return MHD_YES;
3363 0 : case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
3364 0 : GNUNET_break (0);
3365 0 : return MHD_NO;
3366 0 : case GNUNET_JSON_PR_JSON_INVALID:
3367 0 : GNUNET_break (0);
3368 0 : return MHD_NO;
3369 0 : case GNUNET_JSON_PR_SUCCESS:
3370 0 : break;
3371 : }
3372 :
3373 0 : {
3374 : const char *username;
3375 : const char *password;
3376 : struct GNUNET_JSON_Specification spec[] = {
3377 0 : GNUNET_JSON_spec_string ("username",
3378 : &username),
3379 0 : GNUNET_JSON_spec_string ("password",
3380 : &password),
3381 0 : GNUNET_JSON_spec_end ()
3382 : };
3383 : enum GNUNET_GenericReturnValue ret;
3384 :
3385 0 : if (GNUNET_OK !=
3386 0 : (ret = TALER_MHD_parse_json_data (connection,
3387 : json,
3388 : spec)))
3389 : {
3390 0 : GNUNET_break_op (0);
3391 0 : json_decref (json);
3392 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
3393 : }
3394 0 : (void) lookup_account (h,
3395 : username,
3396 : username);
3397 0 : return TALER_MHD_reply_json (connection,
3398 0 : json_object (), /* FIXME: #7301 */
3399 : MHD_HTTP_OK);
3400 : }
3401 : json_decref (json);
3402 : return res;
3403 : }
3404 :
3405 :
3406 : /**
3407 : * Notify long pollers that a @a wo was updated.
3408 : * Must be called with the "big_lock" still held.
3409 : *
3410 : * @param h fakebank handle
3411 : * @param wo withdraw operation that finished
3412 : */
3413 : static void
3414 0 : notify_withdrawal (struct TALER_FAKEBANK_Handle *h,
3415 : const struct WithdrawalOperation *wo)
3416 : {
3417 0 : struct Account *debit_acc = wo->debit_account;
3418 : struct LongPoller *nxt;
3419 :
3420 0 : for (struct LongPoller *lp = debit_acc->lp_head;
3421 : NULL != lp;
3422 0 : lp = nxt)
3423 : {
3424 0 : nxt = lp->next;
3425 0 : if ( (LP_WITHDRAW == lp->type) &&
3426 0 : (wo == lp->wo) )
3427 : {
3428 0 : GNUNET_assert (lp ==
3429 : GNUNET_CONTAINER_heap_remove_node (lp->hn));
3430 0 : lp_trigger (lp,
3431 : h);
3432 : }
3433 : }
3434 0 : }
3435 :
3436 :
3437 : /**
3438 : * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/abort request.
3439 : *
3440 : * @param h our fakebank handle
3441 : * @param connection the connection
3442 : * @param account_name name of the debited account
3443 : * @param withdrawal_id the withdrawal operation identifier
3444 : * @return MHD result code
3445 : */
3446 : static MHD_RESULT
3447 0 : access_withdrawals_abort (struct TALER_FAKEBANK_Handle *h,
3448 : struct MHD_Connection *connection,
3449 : const char *account_name,
3450 : const char *withdrawal_id)
3451 : {
3452 : struct WithdrawalOperation *wo;
3453 : struct Account *acc;
3454 :
3455 0 : GNUNET_assert (0 ==
3456 : pthread_mutex_lock (&h->big_lock));
3457 0 : wo = lookup_withdrawal_operation (h,
3458 : withdrawal_id);
3459 0 : if (NULL == wo)
3460 : {
3461 0 : GNUNET_assert (0 ==
3462 : pthread_mutex_unlock (&h->big_lock));
3463 0 : return TALER_MHD_reply_with_error (connection,
3464 : MHD_HTTP_NOT_FOUND,
3465 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
3466 : withdrawal_id);
3467 : }
3468 0 : acc = lookup_account (h,
3469 : account_name,
3470 : NULL);
3471 0 : if (NULL == acc)
3472 : {
3473 0 : GNUNET_assert (0 ==
3474 : pthread_mutex_unlock (&h->big_lock));
3475 0 : return TALER_MHD_reply_with_error (connection,
3476 : MHD_HTTP_NOT_FOUND,
3477 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
3478 : account_name);
3479 : }
3480 0 : if (wo->debit_account != acc)
3481 : {
3482 0 : GNUNET_assert (0 ==
3483 : pthread_mutex_unlock (&h->big_lock));
3484 0 : return TALER_MHD_reply_with_error (connection,
3485 : MHD_HTTP_NOT_FOUND,
3486 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
3487 : account_name);
3488 : }
3489 0 : if (wo->confirmation_done)
3490 : {
3491 0 : GNUNET_assert (0 ==
3492 : pthread_mutex_unlock (&h->big_lock));
3493 0 : return TALER_MHD_reply_with_error (connection,
3494 : MHD_HTTP_CONFLICT,
3495 : TALER_EC_BANK_ABORT_CONFIRM_CONFLICT,
3496 : account_name);
3497 : }
3498 0 : wo->aborted = true;
3499 0 : notify_withdrawal (h,
3500 : wo);
3501 0 : GNUNET_assert (0 ==
3502 : pthread_mutex_unlock (&h->big_lock));
3503 0 : return TALER_MHD_reply_json (connection,
3504 0 : json_object (), /* FIXME: #7301 */
3505 : MHD_HTTP_OK);
3506 : }
3507 :
3508 :
3509 : /**
3510 : * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/confirm request.
3511 : *
3512 : * @param h our fakebank handle
3513 : * @param connection the connection
3514 : * @param account_name name of the debited account
3515 : * @param withdrawal_id the withdrawal operation identifier
3516 : * @return MHD result code
3517 : */
3518 : static MHD_RESULT
3519 0 : access_withdrawals_confirm (struct TALER_FAKEBANK_Handle *h,
3520 : struct MHD_Connection *connection,
3521 : const char *account_name,
3522 : const char *withdrawal_id)
3523 : {
3524 : struct WithdrawalOperation *wo;
3525 : struct Account *acc;
3526 :
3527 0 : GNUNET_assert (0 ==
3528 : pthread_mutex_lock (&h->big_lock));
3529 0 : wo = lookup_withdrawal_operation (h,
3530 : withdrawal_id);
3531 0 : if (NULL == wo)
3532 : {
3533 0 : GNUNET_assert (0 ==
3534 : pthread_mutex_unlock (&h->big_lock));
3535 0 : return TALER_MHD_reply_with_error (connection,
3536 : MHD_HTTP_NOT_FOUND,
3537 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
3538 : withdrawal_id);
3539 : }
3540 0 : acc = lookup_account (h,
3541 : account_name,
3542 : NULL);
3543 0 : if (NULL == acc)
3544 : {
3545 0 : GNUNET_assert (0 ==
3546 : pthread_mutex_unlock (&h->big_lock));
3547 0 : return TALER_MHD_reply_with_error (connection,
3548 : MHD_HTTP_NOT_FOUND,
3549 : TALER_EC_BANK_UNKNOWN_ACCOUNT,
3550 : account_name);
3551 : }
3552 0 : if (wo->debit_account != acc)
3553 : {
3554 0 : GNUNET_assert (0 ==
3555 : pthread_mutex_unlock (&h->big_lock));
3556 0 : return TALER_MHD_reply_with_error (connection,
3557 : MHD_HTTP_NOT_FOUND,
3558 : TALER_EC_BANK_TRANSACTION_NOT_FOUND,
3559 : account_name);
3560 : }
3561 0 : if (wo->aborted)
3562 : {
3563 0 : GNUNET_assert (0 ==
3564 : pthread_mutex_unlock (&h->big_lock));
3565 0 : return TALER_MHD_reply_with_error (connection,
3566 : MHD_HTTP_CONFLICT,
3567 : TALER_EC_BANK_CONFIRM_ABORT_CONFLICT,
3568 : account_name);
3569 : }
3570 0 : GNUNET_assert (0 ==
3571 : pthread_mutex_unlock (&h->big_lock));
3572 0 : if (GNUNET_OK !=
3573 0 : make_admin_transfer (h,
3574 0 : wo->debit_account->account_name,
3575 0 : wo->exchange_account->account_name,
3576 0 : &wo->amount,
3577 0 : &wo->reserve_pub,
3578 : &wo->row_id,
3579 : &wo->timestamp))
3580 : {
3581 0 : return TALER_MHD_reply_with_error (connection,
3582 : MHD_HTTP_CONFLICT,
3583 : TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
3584 : NULL);
3585 : }
3586 0 : GNUNET_assert (0 ==
3587 : pthread_mutex_lock (&h->big_lock));
3588 0 : wo->confirmation_done = true;
3589 0 : notify_withdrawal (h,
3590 : wo);
3591 0 : GNUNET_assert (0 ==
3592 : pthread_mutex_unlock (&h->big_lock));
3593 0 : return TALER_MHD_reply_json (connection,
3594 0 : json_object (),
3595 : MHD_HTTP_OK);
3596 : }
3597 :
3598 :
3599 : /**
3600 : * Handle incoming HTTP request to the Taler bank access API.
3601 : *
3602 : * @param h our fakebank handle
3603 : * @param connection the connection
3604 : * @param url the requested url
3605 : * @param method the method (POST, GET, ...)
3606 : * @param upload_data request data
3607 : * @param upload_data_size size of @a upload_data in bytes
3608 : * @param con_cls closure for request
3609 : * @return MHD result code
3610 : */
3611 : static MHD_RESULT
3612 0 : handle_bank_access (struct TALER_FAKEBANK_Handle *h,
3613 : struct MHD_Connection *connection,
3614 : const char *url,
3615 : const char *method,
3616 : const char *upload_data,
3617 : size_t *upload_data_size,
3618 : void **con_cls)
3619 : {
3620 0 : if (0 == strcasecmp (method,
3621 : MHD_HTTP_METHOD_HEAD))
3622 0 : method = MHD_HTTP_METHOD_GET;
3623 0 : if ( (0 == strcmp (url,
3624 0 : "/config")) &&
3625 0 : (0 == strcasecmp (method,
3626 : MHD_HTTP_METHOD_GET)) )
3627 : {
3628 0 : return TALER_MHD_REPLY_JSON_PACK (
3629 : connection,
3630 : MHD_HTTP_OK,
3631 : GNUNET_JSON_pack_string ("version",
3632 : "0:0:0"),
3633 : GNUNET_JSON_pack_string ("currency",
3634 : h->currency),
3635 : GNUNET_JSON_pack_string ("name",
3636 : "taler-bank-access"));
3637 : }
3638 0 : if ( (0 == strcmp (url,
3639 0 : "/public-accounts")) &&
3640 0 : (0 == strcasecmp (method,
3641 : MHD_HTTP_METHOD_GET)) )
3642 : {
3643 0 : return TALER_MHD_REPLY_JSON_PACK (
3644 : connection,
3645 : MHD_HTTP_OK,
3646 : GNUNET_JSON_pack_array_steal ("publicAccounts", /* FIXME: #7300 */
3647 : json_array ()));
3648 : }
3649 0 : if ( (0 == strncmp (url,
3650 : "/accounts/",
3651 0 : strlen ("/accounts/"))) &&
3652 0 : (0 == strcasecmp (method,
3653 : MHD_HTTP_METHOD_POST)) )
3654 : {
3655 0 : const char *acc_name = &url[strlen ("/accounts/")];
3656 0 : const char *end_acc = strchr (acc_name,
3657 : '/');
3658 : char *acc;
3659 : MHD_RESULT ret;
3660 :
3661 0 : if ( (NULL == end_acc) ||
3662 0 : (0 != strncmp (end_acc,
3663 : "/withdrawals",
3664 : strlen ("/withdrawals"))) )
3665 : {
3666 0 : GNUNET_break_op (0);
3667 0 : return TALER_MHD_reply_with_error (connection,
3668 : MHD_HTTP_NOT_FOUND,
3669 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
3670 : acc_name);
3671 : }
3672 0 : acc = GNUNET_strndup (acc_name,
3673 : end_acc - acc_name);
3674 0 : end_acc += strlen ("/withdrawals");
3675 0 : if ('/' == *end_acc)
3676 : {
3677 0 : const char *wid = end_acc + 1;
3678 : char *wi;
3679 : const char *opid;
3680 :
3681 0 : if (NULL != end_acc)
3682 0 : opid = strchr (wid,
3683 : '/');
3684 : else
3685 0 : opid = NULL;
3686 0 : if ( (NULL == end_acc) ||
3687 0 : (NULL == opid) ||
3688 0 : ( (0 != strcmp (opid,
3689 0 : "/abort")) &&
3690 0 : (0 != strcmp (opid,
3691 : "/confirm")) ) )
3692 : {
3693 0 : GNUNET_break_op (0);
3694 0 : GNUNET_free (acc);
3695 0 : return TALER_MHD_reply_with_error (connection,
3696 : MHD_HTTP_NOT_FOUND,
3697 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
3698 : acc_name);
3699 : }
3700 0 : wi = GNUNET_strndup (wid,
3701 : opid - wid);
3702 0 : if (0 == strcmp (opid,
3703 : "/abort"))
3704 : {
3705 0 : ret = access_withdrawals_abort (h,
3706 : connection,
3707 : acc,
3708 : wi);
3709 0 : GNUNET_free (wi);
3710 0 : GNUNET_free (acc);
3711 0 : return ret;
3712 : }
3713 0 : if (0 == strcmp (opid,
3714 : "/confirm"))
3715 : {
3716 0 : ret = access_withdrawals_confirm (h,
3717 : connection,
3718 : acc,
3719 : wi);
3720 0 : GNUNET_free (wi);
3721 0 : GNUNET_free (acc);
3722 0 : return ret;
3723 : }
3724 0 : GNUNET_assert (0);
3725 : }
3726 0 : ret = post_account_withdrawals_access (h,
3727 : connection,
3728 : acc,
3729 : upload_data,
3730 : upload_data_size,
3731 : con_cls);
3732 0 : GNUNET_free (acc);
3733 0 : return ret;
3734 : }
3735 :
3736 0 : if ( (0 == strncmp (url,
3737 : "/accounts/",
3738 0 : strlen ("/accounts/"))) &&
3739 0 : (0 == strcasecmp (method,
3740 : MHD_HTTP_METHOD_GET)) )
3741 : {
3742 0 : const char *acc_name = &url[strlen ("/accounts/")];
3743 0 : const char *end_acc = strchr (acc_name,
3744 : '/');
3745 : const char *wid;
3746 : char *acc;
3747 : MHD_RESULT ret;
3748 :
3749 0 : if (NULL == end_acc)
3750 : {
3751 0 : ret = get_account_access (h,
3752 : connection,
3753 : acc_name);
3754 0 : return ret;
3755 : }
3756 0 : if (0 != strncmp (end_acc,
3757 : "/withdrawals/",
3758 : strlen ("/withdrawals/")))
3759 : {
3760 0 : GNUNET_break_op (0);
3761 0 : return TALER_MHD_reply_with_error (connection,
3762 : MHD_HTTP_NOT_FOUND,
3763 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
3764 : acc_name);
3765 : }
3766 0 : acc = GNUNET_strndup (acc_name,
3767 : end_acc - acc_name);
3768 0 : wid = &end_acc[strlen ("/withdrawals/")];
3769 0 : ret = get_account_withdrawals_access (h,
3770 : connection,
3771 : acc,
3772 : wid);
3773 0 : GNUNET_free (acc);
3774 0 : return ret;
3775 : }
3776 : /* FIXME: implement transactions API: 1.12.2 */
3777 :
3778 : /* registration API */
3779 0 : if ( (0 == strcmp (url,
3780 0 : "/testing/register")) &&
3781 0 : (0 == strcasecmp (method,
3782 : MHD_HTTP_METHOD_POST)) )
3783 : {
3784 0 : return post_testing_register (h,
3785 : connection,
3786 : upload_data,
3787 : upload_data_size,
3788 : con_cls);
3789 : }
3790 :
3791 0 : TALER_LOG_ERROR ("Breaking URL: %s %s\n",
3792 : method,
3793 : url);
3794 0 : GNUNET_break_op (0);
3795 0 : return TALER_MHD_reply_with_error (
3796 : connection,
3797 : MHD_HTTP_NOT_FOUND,
3798 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
3799 : url);
3800 : }
3801 :
3802 :
3803 : /**
3804 : * Handle incoming HTTP request.
3805 : *
3806 : * @param cls a `struct TALER_FAKEBANK_Handle`
3807 : * @param connection the connection
3808 : * @param url the requested url
3809 : * @param method the method (POST, GET, ...)
3810 : * @param version HTTP version (ignored)
3811 : * @param upload_data request data
3812 : * @param upload_data_size size of @a upload_data in bytes
3813 : * @param con_cls closure for request
3814 : * @return MHD result code
3815 : */
3816 : static MHD_RESULT
3817 31 : handle_mhd_request (void *cls,
3818 : struct MHD_Connection *connection,
3819 : const char *url,
3820 : const char *method,
3821 : const char *version,
3822 : const char *upload_data,
3823 : size_t *upload_data_size,
3824 : void **con_cls)
3825 : {
3826 31 : struct TALER_FAKEBANK_Handle *h = cls;
3827 31 : char *account = NULL;
3828 : char *end;
3829 : MHD_RESULT ret;
3830 :
3831 : (void) version;
3832 31 : if (0 == strncmp (url,
3833 : "/taler-bank-integration/",
3834 : strlen ("/taler-bank-integration/")))
3835 : {
3836 0 : url += strlen ("/taler-bank-integration");
3837 0 : return handle_bank_integration (h,
3838 : connection,
3839 : url,
3840 : method,
3841 : upload_data,
3842 : upload_data_size,
3843 : con_cls);
3844 : }
3845 31 : if (0 == strncmp (url,
3846 : "/taler-bank-access/",
3847 : strlen ("/taler-bank-access/")))
3848 : {
3849 0 : url += strlen ("/taler-bank-access");
3850 0 : return handle_bank_access (h,
3851 : connection,
3852 : url,
3853 : method,
3854 : upload_data,
3855 : upload_data_size,
3856 : con_cls);
3857 : }
3858 31 : if (0 == strncmp (url,
3859 : "/taler-wire-gateway/",
3860 : strlen ("/taler-wire-gateway/")))
3861 0 : url += strlen ("/taler-wire-gateway");
3862 31 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3863 : "Handling request for `%s'\n",
3864 : url);
3865 31 : if ( (strlen (url) > 1) &&
3866 30 : (NULL != (end = strchr (url + 1, '/'))) )
3867 : {
3868 30 : account = GNUNET_strndup (url + 1,
3869 : end - url - 1);
3870 30 : url = end;
3871 : }
3872 31 : ret = serve (h,
3873 : connection,
3874 : account,
3875 : url,
3876 : method,
3877 : upload_data,
3878 : upload_data_size,
3879 : con_cls);
3880 31 : GNUNET_free (account);
3881 31 : return ret;
3882 : }
3883 :
3884 :
3885 : #if EPOLL_SUPPORT
3886 : /**
3887 : * Schedule MHD. This function should be called initially when an
3888 : * MHD is first getting its client socket, and will then automatically
3889 : * always be called later whenever there is work to be done.
3890 : *
3891 : * @param h fakebank handle to schedule MHD for
3892 : */
3893 : static void
3894 13 : schedule_httpd (struct TALER_FAKEBANK_Handle *h)
3895 : {
3896 : int haveto;
3897 : MHD_UNSIGNED_LONG_LONG timeout;
3898 : struct GNUNET_TIME_Relative tv;
3899 :
3900 13 : GNUNET_assert (-1 != h->mhd_fd);
3901 13 : haveto = MHD_get_timeout (h->mhd_bank,
3902 : &timeout);
3903 13 : if (MHD_YES == haveto)
3904 0 : tv.rel_value_us = (uint64_t) timeout * 1000LL;
3905 : else
3906 13 : tv = GNUNET_TIME_UNIT_FOREVER_REL;
3907 13 : if (NULL != h->mhd_task)
3908 0 : GNUNET_SCHEDULER_cancel (h->mhd_task);
3909 13 : h->mhd_task =
3910 13 : GNUNET_SCHEDULER_add_read_net (tv,
3911 : h->mhd_rfd,
3912 : &run_mhd,
3913 : h);
3914 13 : }
3915 :
3916 :
3917 : #else
3918 : /**
3919 : * Schedule MHD. This function should be called initially when an
3920 : * MHD is first getting its client socket, and will then automatically
3921 : * always be called later whenever there is work to be done.
3922 : *
3923 : * @param h fakebank handle to schedule MHD for
3924 : */
3925 : static void
3926 : schedule_httpd (struct TALER_FAKEBANK_Handle *h)
3927 : {
3928 : fd_set rs;
3929 : fd_set ws;
3930 : fd_set es;
3931 : struct GNUNET_NETWORK_FDSet *wrs;
3932 : struct GNUNET_NETWORK_FDSet *wws;
3933 : int max;
3934 : int haveto;
3935 : MHD_UNSIGNED_LONG_LONG timeout;
3936 : struct GNUNET_TIME_Relative tv;
3937 :
3938 : #ifdef __linux__
3939 : GNUNET_assert (-1 == h->lp_event);
3940 : #else
3941 : GNUNET_assert (-1 == h->lp_event_in);
3942 : GNUNET_assert (-1 == h->lp_event_out);
3943 : #endif
3944 : FD_ZERO (&rs);
3945 : FD_ZERO (&ws);
3946 : FD_ZERO (&es);
3947 : max = -1;
3948 : if (MHD_YES != MHD_get_fdset (h->mhd_bank,
3949 : &rs,
3950 : &ws,
3951 : &es,
3952 : &max))
3953 : {
3954 : GNUNET_assert (0);
3955 : return;
3956 : }
3957 : haveto = MHD_get_timeout (h->mhd_bank,
3958 : &timeout);
3959 : if (MHD_YES == haveto)
3960 : tv.rel_value_us = (uint64_t) timeout * 1000LL;
3961 : else
3962 : tv = GNUNET_TIME_UNIT_FOREVER_REL;
3963 : if (-1 != max)
3964 : {
3965 : wrs = GNUNET_NETWORK_fdset_create ();
3966 : wws = GNUNET_NETWORK_fdset_create ();
3967 : GNUNET_NETWORK_fdset_copy_native (wrs,
3968 : &rs,
3969 : max + 1);
3970 : GNUNET_NETWORK_fdset_copy_native (wws,
3971 : &ws,
3972 : max + 1);
3973 : }
3974 : else
3975 : {
3976 : wrs = NULL;
3977 : wws = NULL;
3978 : }
3979 : if (NULL != h->mhd_task)
3980 : GNUNET_SCHEDULER_cancel (h->mhd_task);
3981 : h->mhd_task =
3982 : GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
3983 : tv,
3984 : wrs,
3985 : wws,
3986 : &run_mhd,
3987 : h);
3988 : if (NULL != wrs)
3989 : GNUNET_NETWORK_fdset_destroy (wrs);
3990 : if (NULL != wws)
3991 : GNUNET_NETWORK_fdset_destroy (wws);
3992 : }
3993 :
3994 :
3995 : #endif
3996 :
3997 :
3998 : /**
3999 : * Task run whenever HTTP server operations are pending.
4000 : *
4001 : * @param cls the `struct TALER_FAKEBANK_Handle`
4002 : */
4003 : static void
4004 12 : run_mhd (void *cls)
4005 : {
4006 12 : struct TALER_FAKEBANK_Handle *h = cls;
4007 :
4008 12 : h->mhd_task = NULL;
4009 12 : h->mhd_again = true;
4010 24 : while (h->mhd_again)
4011 : {
4012 12 : h->mhd_again = false;
4013 12 : MHD_run (h->mhd_bank);
4014 : }
4015 : #ifdef __linux__
4016 12 : GNUNET_assert (-1 == h->lp_event);
4017 : #else
4018 : GNUNET_assert (-1 == h->lp_event_in);
4019 : GNUNET_assert (-1 == h->lp_event_out);
4020 : #endif
4021 12 : schedule_httpd (h);
4022 12 : }
4023 :
4024 :
4025 : struct TALER_FAKEBANK_Handle *
4026 2 : TALER_FAKEBANK_start (uint16_t port,
4027 : const char *currency)
4028 : {
4029 2 : return TALER_FAKEBANK_start2 (port,
4030 : currency,
4031 : 65536, /* RAM limit */
4032 : 1);
4033 : }
4034 :
4035 :
4036 : struct TALER_FAKEBANK_Handle *
4037 2 : TALER_FAKEBANK_start2 (uint16_t port,
4038 : const char *currency,
4039 : uint64_t ram_limit,
4040 : unsigned int num_threads)
4041 : {
4042 2 : return TALER_FAKEBANK_start3 ("localhost",
4043 : port,
4044 : NULL,
4045 : currency,
4046 : ram_limit,
4047 : num_threads);
4048 : }
4049 :
4050 :
4051 : struct TALER_FAKEBANK_Handle *
4052 3 : TALER_FAKEBANK_start3 (const char *hostname,
4053 : uint16_t port,
4054 : const char *exchange_url,
4055 : const char *currency,
4056 : uint64_t ram_limit,
4057 : unsigned int num_threads)
4058 : {
4059 : struct TALER_FAKEBANK_Handle *h;
4060 :
4061 3 : if (SIZE_MAX / sizeof (struct Transaction *) < ram_limit)
4062 : {
4063 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4064 : "This CPU architecture does not support keeping %llu transactions in RAM\n",
4065 : (unsigned long long) ram_limit);
4066 0 : return NULL;
4067 : }
4068 3 : GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN);
4069 3 : h = GNUNET_new (struct TALER_FAKEBANK_Handle);
4070 3 : if (NULL != exchange_url)
4071 0 : h->exchange_url = GNUNET_strdup (exchange_url);
4072 : #ifdef __linux__
4073 3 : h->lp_event = -1;
4074 : #else
4075 : h->lp_event_in = -1;
4076 : h->lp_event_out = -1;
4077 : #endif
4078 : #if EPOLL_SUPPORT
4079 3 : h->mhd_fd = -1;
4080 : #endif
4081 3 : h->port = port;
4082 3 : h->ram_limit = ram_limit;
4083 3 : h->serial_counter = 0;
4084 3 : GNUNET_assert (0 ==
4085 : pthread_mutex_init (&h->accounts_lock,
4086 : NULL));
4087 3 : GNUNET_assert (0 ==
4088 : pthread_mutex_init (&h->rpubs_lock,
4089 : NULL));
4090 3 : GNUNET_assert (0 ==
4091 : pthread_mutex_init (&h->uuid_map_lock,
4092 : NULL));
4093 3 : GNUNET_assert (0 ==
4094 : pthread_mutex_init (&h->big_lock,
4095 : NULL));
4096 : h->transactions
4097 3 : = GNUNET_malloc_large (sizeof (struct Transaction *)
4098 : * ram_limit);
4099 3 : if (NULL == h->transactions)
4100 : {
4101 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
4102 : "malloc");
4103 0 : TALER_FAKEBANK_stop (h);
4104 0 : return NULL;
4105 : }
4106 3 : h->accounts = GNUNET_CONTAINER_multihashmap_create (128,
4107 : GNUNET_NO);
4108 3 : h->uuid_map = GNUNET_CONTAINER_multihashmap_create (ram_limit * 4 / 3,
4109 : GNUNET_YES);
4110 3 : if (NULL == h->uuid_map)
4111 : {
4112 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
4113 : "malloc");
4114 0 : TALER_FAKEBANK_stop (h);
4115 0 : return NULL;
4116 : }
4117 3 : h->rpubs = GNUNET_CONTAINER_multipeermap_create (ram_limit * 4 / 3,
4118 : GNUNET_NO);
4119 3 : if (NULL == h->rpubs)
4120 : {
4121 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
4122 : "malloc");
4123 0 : TALER_FAKEBANK_stop (h);
4124 0 : return NULL;
4125 : }
4126 3 : h->lp_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
4127 3 : h->currency = GNUNET_strdup (currency);
4128 3 : h->hostname = GNUNET_strdup (hostname);
4129 3 : GNUNET_asprintf (&h->my_baseurl,
4130 : "http://%s:%u/",
4131 : h->hostname,
4132 : (unsigned int) port);
4133 3 : if (0 == num_threads)
4134 : {
4135 1 : h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
4136 : #if EPOLL_SUPPORT
4137 : | MHD_USE_EPOLL
4138 : #endif
4139 : | MHD_USE_DUAL_STACK
4140 : | MHD_ALLOW_SUSPEND_RESUME,
4141 : port,
4142 : NULL, NULL,
4143 : &handle_mhd_request, h,
4144 : MHD_OPTION_NOTIFY_COMPLETED,
4145 : &handle_mhd_completion_callback, h,
4146 : MHD_OPTION_LISTEN_BACKLOG_SIZE,
4147 : (unsigned int) 1024,
4148 : MHD_OPTION_CONNECTION_LIMIT,
4149 : (unsigned int) 65536,
4150 : MHD_OPTION_END);
4151 1 : if (NULL == h->mhd_bank)
4152 : {
4153 0 : TALER_FAKEBANK_stop (h);
4154 0 : return NULL;
4155 : }
4156 : #if EPOLL_SUPPORT
4157 1 : h->mhd_fd = MHD_get_daemon_info (h->mhd_bank,
4158 1 : MHD_DAEMON_INFO_EPOLL_FD)->epoll_fd;
4159 1 : h->mhd_rfd = GNUNET_NETWORK_socket_box_native (h->mhd_fd);
4160 : #endif
4161 1 : schedule_httpd (h);
4162 : }
4163 : else
4164 : {
4165 : #ifdef __linux__
4166 2 : h->lp_event = eventfd (0,
4167 : EFD_CLOEXEC);
4168 2 : if (-1 == h->lp_event)
4169 : {
4170 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
4171 : "eventfd");
4172 0 : TALER_FAKEBANK_stop (h);
4173 0 : return NULL;
4174 : }
4175 : #else
4176 : {
4177 : int pipefd[2];
4178 :
4179 : if (0 != pipe (pipefd))
4180 : {
4181 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
4182 : "pipe");
4183 : TALER_FAKEBANK_stop (h);
4184 : return NULL;
4185 : }
4186 : h->lp_event_out = pipefd[0];
4187 : h->lp_event_in = pipefd[1];
4188 : }
4189 : #endif
4190 2 : if (0 !=
4191 2 : pthread_create (&h->lp_thread,
4192 : NULL,
4193 : &lp_expiration_thread,
4194 : h))
4195 : {
4196 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
4197 : "pthread_create");
4198 : #ifdef __linux__
4199 0 : GNUNET_break (0 == close (h->lp_event));
4200 0 : h->lp_event = -1;
4201 : #else
4202 : GNUNET_break (0 == close (h->lp_event_in));
4203 : GNUNET_break (0 == close (h->lp_event_out));
4204 : h->lp_event_in = -1;
4205 : h->lp_event_out = -1;
4206 : #endif
4207 0 : TALER_FAKEBANK_stop (h);
4208 0 : return NULL;
4209 : }
4210 2 : h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
4211 : | MHD_USE_AUTO_INTERNAL_THREAD
4212 : | MHD_ALLOW_SUSPEND_RESUME
4213 : | MHD_USE_TURBO
4214 : | MHD_USE_TCP_FASTOPEN
4215 : | MHD_USE_DUAL_STACK,
4216 : port,
4217 : NULL, NULL,
4218 : &handle_mhd_request, h,
4219 : MHD_OPTION_NOTIFY_COMPLETED,
4220 : &handle_mhd_completion_callback, h,
4221 : MHD_OPTION_LISTEN_BACKLOG_SIZE,
4222 : (unsigned int) 1024,
4223 : MHD_OPTION_CONNECTION_LIMIT,
4224 : (unsigned int) 65536,
4225 : MHD_OPTION_THREAD_POOL_SIZE,
4226 : num_threads,
4227 : MHD_OPTION_END);
4228 2 : if (NULL == h->mhd_bank)
4229 : {
4230 0 : GNUNET_break (0);
4231 0 : TALER_FAKEBANK_stop (h);
4232 0 : return NULL;
4233 : }
4234 : }
4235 3 : return h;
4236 : }
4237 :
4238 :
4239 : /* end of fakebank.c */
|