LCOV - code coverage report
Current view: top level - bank-lib - fakebank.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 439 1162 37.8 %
Date: 2022-08-25 06:15:09 Functions: 23 46 50.0 %
Legend: Lines: hit not hit

          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             :                                &timestamp);
    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 */

Generated by: LCOV version 1.14