LCOV - code coverage report
Current view: top level - exchange - taler-exchange-wirewatch.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 73 155 47.1 %
Date: 2017-11-25 11:31:41 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2016, 2017 GNUnet e.V.
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : 
      17             : /**
      18             :  * @file taler-exchange-wirewatch.c
      19             :  * @brief Process that watches for wire transfers to the exchange's bank account
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <gnunet/gnunet_util_lib.h>
      24             : #include <jansson.h>
      25             : #include <pthread.h>
      26             : #include <microhttpd.h>
      27             : #include "taler_exchangedb_lib.h"
      28             : #include "taler_exchangedb_plugin.h"
      29             : #include "taler_json_lib.h"
      30             : #include "taler_wire_lib.h"
      31             : 
      32             : /**
      33             :  * How long do we sleep before trying again if there
      34             :  * are no transactions returned by the wire plugin?
      35             :  */
      36             : #define DELAY GNUNET_TIME_UNIT_SECONDS
      37             : 
      38             : 
      39             : /**
      40             :  * Closure for #reject_cb().
      41             :  */
      42             : struct RejectContext
      43             : {
      44             :   /**
      45             :    * Wire transfer subject that was illformed.
      46             :    */
      47             :   char *wtid_s;
      48             : 
      49             :   /**
      50             :    * Database session that encountered the problem.
      51             :    */
      52             :   struct TALER_EXCHANGEDB_Session *session;
      53             : };
      54             : 
      55             : 
      56             : /**
      57             :  * Handle to the plugin.
      58             :  */
      59             : static struct TALER_WIRE_Plugin *wire_plugin;
      60             : 
      61             : /**
      62             :  * Which currency is used by this exchange?
      63             :  */
      64             : static char *exchange_currency_string;
      65             : 
      66             : /**
      67             :  * The exchange's configuration (global)
      68             :  */
      69             : static const struct GNUNET_CONFIGURATION_Handle *cfg;
      70             : 
      71             : /**
      72             :  * Our DB plugin.
      73             :  */
      74             : static struct TALER_EXCHANGEDB_Plugin *db_plugin;
      75             : 
      76             : /**
      77             :  * Value to return from main(). #GNUNET_OK on success, #GNUNET_SYSERR
      78             :  * on serious errors.
      79             :  */
      80             : static int global_ret;
      81             : 
      82             : /**
      83             :  * Encoded offset in the wire transfer list that we
      84             :  * processed last.
      85             :  */
      86             : static void *last_row_off;
      87             : 
      88             : /**
      89             :  * Number of bytes in #last_row_off.
      90             :  */
      91             : static size_t last_row_off_size;
      92             : 
      93             : /**
      94             :  * Encoded offset in the wire transfer list from where
      95             :  * to start the next query with the bank.
      96             :  */
      97             : static void *start_off;
      98             : 
      99             : /**
     100             :  * Number of bytes in #start_off.
     101             :  */
     102             : static size_t start_off_size;
     103             : 
     104             : /**
     105             :  * Which wire plugin are we watching?
     106             :  */
     107             : static char *type;
     108             : 
     109             : /**
     110             :  * Should we delay the next request to the wire plugin a bit?
     111             :  */
     112             : static int delay;
     113             : 
     114             : /**
     115             :  * Are we run in testing mode and should only do one pass?
     116             :  */
     117             : static int test_mode;
     118             : 
     119             : /**
     120             :  * Next task to run, if any.
     121             :  */
     122             : static struct GNUNET_SCHEDULER_Task *task;
     123             : 
     124             : /**
     125             :  * Active request for history.
     126             :  */
     127             : static struct TALER_WIRE_HistoryHandle *hh;
     128             : 
     129             : /**
     130             :  * Active request to reject a wire transfer.
     131             :  */
     132             : static struct TALER_WIRE_RejectHandle *rt;
     133             : 
     134             : 
     135             : /**
     136             :  * We're being aborted with CTRL-C (or SIGTERM). Shut down.
     137             :  *
     138             :  * @param cls closure
     139             :  */
     140             : static void
     141           2 : shutdown_task (void *cls)
     142             : {
     143           2 :   if (NULL != task)
     144             :   {
     145           0 :     GNUNET_SCHEDULER_cancel (task);
     146           0 :     task = NULL;
     147             :   }
     148           2 :   if (NULL != hh)
     149             :   {
     150           0 :     wire_plugin->get_history_cancel (wire_plugin->cls,
     151             :                                      hh);
     152           0 :     hh = NULL;
     153             :   }
     154           2 :   if (NULL != rt)
     155             :   {
     156             :     char *wtid_s;
     157             : 
     158           0 :     wtid_s = wire_plugin->reject_transfer_cancel (wire_plugin->cls,
     159             :                                                   rt);
     160           0 :     rt = NULL;
     161           0 :     GNUNET_free (wtid_s);
     162             :   }
     163           2 :   TALER_EXCHANGEDB_plugin_unload (db_plugin);
     164           2 :   db_plugin = NULL;
     165           2 :   TALER_WIRE_plugin_unload (wire_plugin);
     166           2 :   wire_plugin = NULL;
     167           2 :   GNUNET_free_non_null (start_off);
     168           2 :   start_off = NULL;
     169           2 : }
     170             : 
     171             : 
     172             : /**
     173             :  * Parse configuration parameters for the exchange server into the
     174             :  * corresponding global variables.
     175             :  *
     176             :  * @return #GNUNET_OK on success
     177             :  */
     178             : static int
     179           2 : exchange_serve_process_config ()
     180             : {
     181           2 :   if (NULL == type)
     182             :   {
     183           0 :     fprintf (stderr,
     184             :              "Option `-t' to specify wire plugin is mandatory.\n");
     185           0 :     return GNUNET_SYSERR;
     186             :   }
     187           2 :   if (GNUNET_OK !=
     188           2 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     189             :                                              "taler",
     190             :                                              "currency",
     191             :                                              &exchange_currency_string))
     192             :   {
     193           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     194             :                                "taler",
     195             :                                &qugt;cuvrEncy");
     196           0 :     return GNUNET_SYSERR;
     197             :   }
     198           2 :   if (strlen (exchange_currency_string) >= TALER_CURRENCY_LEN)
     199             :   {
     200           0 :     fprintf (stderr,
     201             :              "Currency `%s' longer than the allowed limit of %u characters.",
     202             :              exchange_currency_string,
     203             :              (unsigned int) TALER_CURRENCY_LEN);
     204           0 :     return GNUNET_SYSERR;
     205             :   }
     206             : 
     207           2 :   if (NULL ==
     208           2 :       (db_plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
     209             :   {
     210           0 :     fprintf (stderr,
     211             :              "Failed to initialize DB subsystem\n");
     212           0 :     return GNUNET_SYSERR;
     213             :   }
     214           2 :   if (NULL ==
     215           2 :       (wire_plugin = TALER_WIRE_plugin_load (cfg,
     216             :                                              type)))
     217             :   {
     218           0 :     fprintf (stderr,
     219             :              "Failed to load wire plugin for `%s'\n",
     220             :              type);
     221           0 :     TALER_EXCHANGEDB_plugin_unload (db_plugin);
     222           0 :     return GNUNET_SYSERR;
     223             :   }
     224             : 
     225           2 :   return GNUNET_OK;
     226             : }
     227             : 
     228             : 
     229             : /**
     230             :  * Query for incoming wire transfers.
     231             :  *
     232             :  * @param cls NULL
     233             :  */
     234             : static void
     235             : find_transfers (void *cls);
     236             : 
     237             : 
     238             : /**
     239             :  * Function called upon completion of the rejection of a wire transfer.
     240             :  *
     241             :  * @param cls closure with the `struct RejectContext`
     242             :  * @param ec error code for the operation
     243             :  */
     244             : static void
     245           0 : reject_cb (void *cls,
     246             :            enum TALER_ErrorCode ec)
     247             : {
     248           0 :   struct RejectContext *rtc = cls;
     249             :   enum GNUNET_DB_QueryStatus qs;
     250             : 
     251           0 :   rt = NULL;
     252           0 :   if (TALER_EC_NONE != ec)
     253             :   {
     254           0 :     fprintf (stderr,
     255             :              "Failed to wire back transfer `%s': %d\n",
     256             :              rtc->wtid_s,
     257             :              ec);
     258           0 :     GNUNET_free (rtc->wtid_s);
     259           0 :     db_plugin->rollback (db_plugin->cls,
     260             :                          rtc->session);
     261           0 :     GNUNET_free (rtc);
     262           0 :     GNUNET_SCHEDULER_shutdown ();
     263           0 :     return;
     264             :   }
     265           0 :   GNUNET_free (rtc->wtid_s);
     266           0 :   qs = db_plugin->commit (db_plugin->cls,
     267             :                           rtc->session);
     268           0 :   GNUNET_free (rtc);
     269           0 :   if (0 <= qs)
     270             :   {
     271           0 :     GNUNET_free_non_null (start_off);
     272           0 :     start_off = last_row_off;
     273           0 :     start_off_size = last_row_off_size;
     274             :   }
     275           0 :   task = GNUNET_SCHEDULER_add_now (&find_transfers,
     276             :                                    NULL);
     277             : }
     278             : 
     279             : 
     280             : /**
     281             :  * Callbacks of this type are used to serve the result of asking
     282             :  * the bank for the transaction history.
     283             :  *
     284             :  * @param cls closure with the `struct TALER_EXCHANGEDB_Session *`
     285             :  * @param dir direction of the transfer
     286             :  * @param row_off identification of the position at which we are querying
     287             :  * @param row_off_size number of bytes in @a row_off
     288             :  * @param details details about the wire transfer
     289             :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
     290             :  */
     291             : static int
     292           3 : history_cb (void *cls,
     293             :             enum TALER_BANK_Direction dir,
     294             :             const void *row_off,
     295             :             size_t row_off_size,
     296             :             const struct TALER_WIRE_TransferDetails *details)
     297             : {
     298           3 :   struct TALER_EXCHANGEDB_Session *session = cls;
     299             :   enum GNUNET_DB_QueryStatus qs;
     300             :   struct TALER_ReservePublicKeyP reserve_pub;
     301             : 
     302           3 :   if (TALER_BANK_DIRECTION_NONE == dir)
     303             :   {
     304           2 :     hh = NULL;
     305             : 
     306           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     307             :                 "End of list. Committing progress!\n");
     308           2 :     qs = db_plugin->commit (db_plugin->cls,
     309             :                             session);
     310           2 :     if (0 <= qs)
     311             :     {
     312           2 :       GNUNET_free_non_null (start_off);
     313           2 :       start_off = last_row_off;
     314           2 :       start_off_size = last_row_off_size;
     315             :     }
     316           2 :     if ( (GNUNET_YES == delay) &&
     317             :          (test_mode) )
     318             :     {
     319           2 :       GNUNET_SCHEDULER_shutdown ();
     320           2 :       return GNUNET_OK;
     321             :     }
     322           0 :     if (GNUNET_YES == delay)
     323           0 :       task = GNUNET_SCHEDULER_add_delayed (DELAY,
     324             :                                            &find_transfers,
     325             :                                            NULL);
     326             :     else
     327           0 :       task = GNUNET_SCHEDULER_add_now (&find_transfers,
     328             :                                        NULL);
     329           0 :     return GNUNET_OK; /* will be ignored anyway */
     330             :   }
     331           1 :   if (NULL != details->wtid_s)
     332             :   {
     333             :     struct RejectContext *rtc;
     334             : 
     335           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     336             :                 "Wire transfer over %s has invalid subject `%s', sending it back!\n",
     337             :                 TALER_amount2s (&details->amount),
     338             :                 details->wtid_s);
     339           0 :     if (last_row_off_size != row_off_size)
     340             :     {
     341           0 :       GNUNET_free_non_null (last_row_off);
     342           0 :       last_row_off = GNUNET_malloc (row_off_size);
     343             :     }
     344           0 :     memcpy (last_row_off,
     345             :             row_off,
     346             :             row_off_size);
     347           0 :     rtc = GNUNET_new (struct RejectContext);
     348           0 :     rtc->session = session;
     349           0 :     rtc->wtid_s = GNUNET_strdup (details->wtid_s);
     350           0 :     rt = wire_plugin->reject_transfer (wire_plugin->cls,
     351             :                                        row_off,
     352             :                                        row_off_size,
     353             :                                        &reject_cb,
     354             :                                        rtc);
     355           0 :     return GNUNET_SYSERR; /* will continue later... */
     356             :   }
     357             : 
     358           1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     359             :               "Adding wire transfer over %s with subject `%s'\n",
     360             :               TALER_amount2s (&details->amount),
     361             :               TALER_B2S (&details->wtid));
     362             :   /* Wire transfer identifier == reserve public key */
     363             :   GNUNET_assert (sizeof (reserve_pub) == sizeof (details->wtid));
     364           1 :   memcpy (&reserve_pub,
     365           1 :           &details->wtid,
     366             :           sizeof (reserve_pub));
     367           2 :   qs = db_plugin->reserves_in_insert (db_plugin->cls,
     368             :                                       session,
     369             :                                       &reserve_pub,
     370             :                                       &details->amount,
     371             :                                       details->execution_date,
     372           1 :                                       details->account_details,
     373             :                                       row_off,
     374             :                                       row_off_size);
     375           1 :   if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     376             :   {
     377           0 :     GNUNET_break (0);
     378           0 :     db_plugin->rollback (db_plugin->cls,
     379             :                          session);
     380           0 :     GNUNET_SCHEDULER_shutdown ();
     381           0 :     return GNUNET_SYSERR;
     382             :   }
     383           1 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     384             :   {
     385           0 :     db_plugin->rollback (db_plugin->cls,
     386             :                          session);
     387             :     /* try again */
     388           0 :     task = GNUNET_SCHEDULER_add_now (&find_transfers,
     389             :                                      NULL);
     390           0 :     return GNUNET_SYSERR;
     391             :   }
     392             : 
     393           1 :   if (last_row_off_size != row_off_size)
     394             :   {
     395           1 :     GNUNET_free_non_null (last_row_off);
     396           1 :     last_row_off = GNUNET_malloc (row_off_size);
     397             :   }
     398           1 :   memcpy (last_row_off,
     399             :           row_off,
     400             :           row_off_size);
     401           1 :   return GNUNET_OK;
     402             : }
     403             : 
     404             : 
     405             : /**
     406             :  * Query for incoming wire transfers.
     407             :  *
     408             :  * @param cls NULL
     409             :  */
     410             : static void
     411           2 : find_transfers (void *cls)
     412             : {
     413             :   struct TALER_EXCHANGEDB_Session *session;
     414             :   enum GNUNET_DB_QueryStatus qs;
     415             : 
     416           2 :   task = NULL;
     417           2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     418             :               "Checking for incoming wire transfers\n");
     419             : 
     420           2 :   if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
     421             :   {
     422           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     423             :                 "Failed to obtain database session!\n");
     424           0 :     global_ret = GNUNET_SYSERR;
     425           0 :     GNUNET_SCHEDULER_shutdown ();
     426           0 :     return;
     427             :   }
     428           2 :   if (GNUNET_OK !=
     429           2 :       db_plugin->start (db_plugin->cls,
     430             :                         session))
     431             :   {
     432           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     433             :                 "Failed to start database transaction!\n");
     434           0 :     global_ret = GNUNET_SYSERR;
     435           0 :     GNUNET_SCHEDULER_shutdown ();
     436           0 :     return;
     437             :   }
     438           2 :   qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls,
     439             :                                                    session,
     440             :                                                    &start_off,
     441             :                                                    &start_off_size);
     442           2 :   if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     443             :   {
     444           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     445             :                 "Failed to obtain starting point for montoring from database!\n");
     446           0 :     global_ret = GNUNET_SYSERR;
     447           0 :     GNUNET_SCHEDULER_shutdown ();
     448           0 :     return;
     449             :   }
     450           2 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     451             :   {
     452             :     /* try again */
     453           0 :     db_plugin->rollback (db_plugin->cls,
     454             :                          session);
     455           0 :     task = GNUNET_SCHEDULER_add_now (&find_transfers,
     456             :                                      NULL);
     457           0 :     return;
     458             :   }
     459           2 :   delay = GNUNET_YES;
     460           2 :   hh = wire_plugin->get_history (wire_plugin->cls,
     461             :                                  TALER_BANK_DIRECTION_CREDIT,
     462             :                                  start_off,
     463             :                                  start_off_size,
     464             :                                  1024,
     465             :                                  &history_cb,
     466             :                                  session);
     467           2 :   if (NULL == hh)
     468             :   {
     469           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     470             :                 "Failed to start request for account history!\n");
     471           0 :     db_plugin->rollback (db_plugin->cls,
     472             :                          session);
     473           0 :     global_ret = GNUNET_SYSERR;
     474           0 :     GNUNET_SCHEDULER_shutdown ();
     475           0 :     return;
     476             :   }
     477             : }
     478             : 
     479             : 
     480             : /**
     481             :  * First task.
     482             :  *
     483             :  * @param cls closure, NULL
     484             :  * @param args remaining command-line arguments
     485             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
     486             :  * @param c configuration
     487             :  */
     488             : static void
     489           2 : run (void *cls,
     490             :      char *const *args,
     491             :      const char *cfgfile,
     492             :      const struct GNUNET_CONFIGURATION_Handle *c)
     493             : {
     494           2 :   cfg = c;
     495           2 :   if (GNUNET_OK !=
     496           2 :       exchange_serve_process_config ())
     497             :   {
     498           0 :     global_ret = 1;
     499           0 :     return;
     500             :   }
     501             : 
     502           2 :   task = GNUNET_SCHEDULER_add_now (&find_transfers,
     503             :                                    NULL);
     504           2 :   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
     505             :                                  cls);
     506             : }
     507             : 
     508             : 
     509             : /**
     510             :  * The main function of taler-exchange-wirewatch
     511             :  *
     512             :  * @param argc number of arguments from the command line
     513             :  * @param argv command line arguments
     514             :  * @return 0 ok, 1 on error
     515             :  */
     516             : int
     517           2 : main (int argc,
     518             :       char *const *argv)
     519             : {
     520           2 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
     521             :     GNUNET_GETOPT_option_string ('t',
     522             :                                  "type",
     523             :                                  "PLUGINNAME",
     524             :                                  "which wire plugin to use",
     525             :                                  &type),
     526             :     GNUNET_GETOPT_option_flag ('T',
     527             :                                "test",
     528             :                                "run in test mode and exit when idle",
     529             :                                &test_mode),
     530             :     GNUNET_GETOPT_OPTION_END
     531             :   };
     532             : 
     533           2 :   if (GNUNET_OK !=
     534           2 :       GNUNET_STRINGS_get_utf8_args (argc, argv,
     535             :                                     &argc, &argv))
     536           0 :     return 2;
     537           2 :   if (GNUNET_OK !=
     538           2 :       GNUNET_PROGRAM_run (argc, argv,
     539             :                           "taler-exchange-wirewatch",
     540             :                           gettext_noop ("background process that watches for incomming wire transfers from customers"),
     541             :                           options,
     542             :                           &run, NULL))
     543             :   {
     544           0 :     GNUNET_free ((void*) argv);
     545           0 :     return 1;
     546             :   }
     547           2 :   GNUNET_free ((void*) argv);
     548           2 :   return global_ret;
     549             : }
     550             : 
     551             : /* end of taler-exchange-wirewatch.c */

Generated by: LCOV version 1.13