cash2ecash

cash2ecash: cash acceptor that issues digital cash (experimental)
Log | Files | Refs | README | LICENSE

taler-digitizer.c (38974B)


      1 /*
      2   This file is part of TALER cash2ecash
      3   Copyright (C) 2026 GNUnet e.V.
      4 
      5   This program is free software: you can redistribute it and/or modify
      6   it under the terms of the GNU Affero General Public License as
      7   published by the Free Software Foundation, either version 3 of the
      8   License, or (at your option) any later version.
      9 
     10   This program is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14   GNU Affero General Public License for more details.
     15 
     16   You should have received a copy of the GNU Affero General Public License
     17   along with this program.  If not, see <https://www.gnu.org/licenses/>.
     18 */
     19 
     20 /**
     21  * @file taler-digitizer.c
     22  * @brief runs the logic for the embeded Cash Digitizer machine
     23  * @author Reto Tellenbach
     24  */
     25 
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <signal.h>
     29 #include <gnunet/gnunet_util_lib.h>
     30 #include "taler_digitizer_util.h"
     31 #include "taler/taler_digitizer_service.h"
     32 #include "lib/bank_api_get_config.h"
     33 #include "lib/bank_api_get_accounts.h"
     34 #include "lib/bank_api_post_accounts_withdrawals.h"
     35 #include "lib/bank_api_get_withdrawals.h"
     36 #include "lib/bank_api_post_accounts_withdrawals_confirm.h"
     37 #include "digitizer_display.h"
     38 #include "gpio/gpiod_wrapper.h"
     39 
     40 
     41 /**
     42  * Time unit for PERSON_WITHDRAL_PERIOD config.
     43  * Normaly in Days but, can be changed for testing
     44  */
     45 #define DIGITIZER_PERSON_WITHDRAWAL_PERIOD_TIME_UNIT  GNUNET_TIME_UNIT_DAYS
     46 
     47 #define FRAMEBUFFER_SIZE 256
     48 #define PATH_QR_SHOW "/ext/QRshow"
     49 
     50 #define SCAN_QR_TIMEOUT_SECONDS 60
     51 #define DISPLAY_BLOCK_SIZE 1024
     52 #define DISPLAY_COUNT 750
     53 
     54 /**
     55  * Global return value
     56  */
     57 static int global_ret;
     58 
     59 /**
     60  * Global option '-d' to enable diagnostics set.
     61  */
     62 static int enable_diagnostics;
     63 
     64 /**
     65  * Taler bank backend url read from configuration file
     66  */
     67 static char *cfg_bank_base_url;
     68 
     69 /**
     70  * Taler bank account username
     71  */
     72 static char *cfg_bank_account_username;
     73 
     74 /**
     75  * Taler bank authentication
     76  * method and according data
     77  */
     78 static struct DIGITIZER_BankAuthenticationData *cfg_bank_authentication;
     79 
     80 /**
     81  * Taler exchange backend url read from configuration file
     82  */
     83 static char *cfg_exchange_base_url;
     84 
     85 /**
     86  * Currency read from configuration file
     87  */
     88 static char *cfg_currency;
     89 
     90 /**
     91  * Per-person withdrawal limit read from configuration file
     92  */
     93 static struct TALER_Amount cfg_user_withdrawallimit;
     94 
     95 /**
     96  * Per-person withdrawal period read from configuration file
     97  */
     98 static struct GNUNET_TIME_Relative cfg_user_withdrawal_period;
     99 
    100 /**
    101  * KYC functionality flag read from configuration file
    102  */
    103 static enum GNUNET_GenericReturnValue cfg_kyc_functionality;
    104 
    105 /**
    106  * device path to display framebuffer.
    107  * usualy in /dev/fbX.
    108  */
    109 static char *cfg_framebuffer_device;
    110 
    111 /**
    112  * device path to display backlight.
    113  * usualy in /sys/class/backlight/<XX>/brightness
    114  */
    115 static char *cfg_framebuffer_backlight;
    116 
    117 /**
    118  * Coin Acceptor installed
    119  */
    120 static enum GNUNET_GenericReturnValue cfg_ca_enable;
    121 
    122 /**
    123  * device path to coin acceptor interface
    124  */
    125 static char *cfg_ca_device;
    126 
    127 /**
    128  * Pin to enable Accepting coins
    129  */
    130 static unsigned long long cfg_ca_en_pin;
    131 
    132 /**
    133  * Pin to recive inserted amount signal
    134  */
    135 static unsigned long long cfg_ca_rx_pin;
    136 
    137 /**
    138  * Biggest value of one coin insertion
    139  */
    140 static struct TALER_Amount cfg_ca_max_denomination;
    141 
    142 /**
    143  * Smallest value of one coin insertion
    144  */
    145 static struct TALER_Amount cfg_ca_min_denomination;
    146 
    147 /**
    148  * Bill Acceptor installed
    149  */
    150 static enum GNUNET_GenericReturnValue cfg_ba_enable;
    151 
    152 /**
    153  * device path to bill acceptor interface
    154  */
    155 static char *cfg_ba_device;
    156 
    157 /**
    158  * Biggest value of one bill insertion
    159  */
    160 static struct TALER_Amount cfg_ba_max_denomination;
    161 
    162 /**
    163  * Smallest value of one bill insertion
    164  */
    165 static struct TALER_Amount cfg_ba_min_denomination;
    166 
    167 
    168 
    169 /**
    170  * Handle to the context for http requests
    171  */
    172 static struct GNUNET_CURL_Context *curl_ctx;
    173 
    174 /**
    175  * Scheduler context for running the @e ctx.
    176  */
    177 static struct GNUNET_CURL_RescheduleContext *reschedule_ctx;
    178 
    179 /**
    180  * Handle for get_config request
    181  */
    182 static struct TALER_BANK_GetConfigHandle *get_config_handle;
    183 
    184 /**
    185  * Handle for get_accounts request
    186  */
    187 static struct TALER_BANK_GetAccountsHandle *get_accounts_handle;
    188 
    189 /**
    190  * Handle for get_accounts request
    191  */
    192 static struct TALER_BANK_GetWithdrawalHandle *get_withdrawal_handle;
    193 
    194 /**
    195  * Handle for post_accounts_withdrawal request
    196  */
    197 static struct TALER_BANK_PostCreateWithdrawalHandle *post_accounts_withdrawal_handle;
    198 
    199 /**
    200  * used to init gpio
    201  */
    202 static struct gpiod_line_settings *gpio_settings;
    203 
    204 /**
    205  * gpio request to enable coin acceping
    206  */
    207 static struct gpiod_line_request *rl_ca_enable;
    208 
    209 
    210 
    211 enum
    212 DIGITIZER_states
    213 {
    214   /**
    215    * Init by loading configs
    216    */
    217   DIGITIZER_STATE_INIT,
    218 
    219   /**
    220    * 
    221    */
    222   DIGITIZER_STATE_IDLE,
    223 
    224   /**
    225    * 
    226    */
    227   DIGITIZER_STATE_CREATE_QR,
    228 
    229   /**
    230    * 
    231    */
    232   DIGITIZER_STATE_SCAN_QR,
    233 
    234   /**
    235    * 
    236    */
    237   DIGITIZER_STATE_IDENTIFICATION,
    238   /**
    239    * 
    240    */
    241   DIGITIZER_STATE_COUNT_MONEY,
    242   /**
    243    * 
    244    */
    245   DIGITIZER_STATE_ACCEPT_CASH,
    246   /**
    247    * 
    248    */
    249   DIGITIZER_STATE_CASHING_UP,
    250   /**
    251    * 
    252    */
    253   DIGITIZER_STATE_WITHDRAWAL_STATUS,
    254   /**
    255    * 
    256    */
    257   DIGITIZER_STATE_CANCEL_WITHDRAWAL,
    258   /**
    259    * 
    260    */
    261   DIGITIZER_STATE_WITHDRAWAL_ERROR,
    262   /**
    263    * 
    264    */
    265   DIGITIZER_STATE_TERMINAL_ERROR,
    266 
    267   /**
    268    * count used to initialise state table with size
    269    */
    270   DIGITIZER_STATE_NUMBER_OF_STATES
    271 };
    272 
    273 /**
    274  * Data moved between states
    275  */
    276 struct DIGITIZER_StateContext
    277 {
    278   enum TALER_BANK_VersionCompatibility version_compa;
    279 
    280   enum TALLER_BANK_CurrencyCompatibility currency_compa;
    281 
    282   /**
    283    * digitizer defined max amount for one insertion action,
    284    * sum from bill- and coin-acceptor
    285    */
    286   struct TALER_Amount max_insertion_amount;
    287 
    288   /**
    289    * digitizer defined min amount for one insertion action,
    290    * min from bill-/coin-acceptor
    291    */
    292   struct TALER_Amount min_insertion_amount;
    293 
    294   /**
    295    * max_wire_transfer_amount of bank config
    296    */
    297   struct TALER_Amount bank_max_wire_transfer_amount;
    298 
    299   /**
    300    * amount avaliable for current withdrawal process
    301    * is updated according to bank balance and bank withdrawal-limits
    302    * in init state of each withdrawal process
    303    */
    304   struct TALER_Amount withdrawal_limit;
    305 
    306   /**
    307    * withdrawal operation information
    308    * url and id
    309    */
    310   struct TALER_BANK_CreateWithdrawalInformatio wi;
    311 
    312   /**
    313    * amount of current prozessing withdrawal
    314    */
    315   struct TALER_Amount digitizer_user_balance;
    316 };
    317 
    318 static enum DIGITIZER_states state;
    319 
    320 struct DIGITIZER_StateContext *state_ctx;
    321 
    322 struct DIGITIZER_DisplayContext *display_ctx;
    323 
    324 static void state_controller_task(void *cls);
    325 
    326 
    327 void
    328 start_qr_show(int showtime)
    329 {
    330   //char buffer[FRAMEBUFFER_SIZE];
    331   char *command;
    332   char *url;
    333   char *path;
    334 
    335   const char *prefix = getenv("TALER_DIGITIZER_PREFIX");
    336   if (NULL != prefix)
    337     GNUNET_asprintf (&path, "%s%s", prefix,PATH_QR_SHOW);
    338   else
    339     path = GNUNET_strdup ("/usr/local/bin/taler-digitizer-qr-show");
    340   url = TALER_url_join(state_ctx->wi.taler_withdraw_uri,
    341                 "",
    342                 "external-confirmation",
    343                 "1",
    344                 NULL);
    345   GNUNET_asprintf(&command,
    346                   "%s -c taler-digitizer.conf -d \"%d s\" %s\n",
    347                   path,
    348                   showtime,
    349                   url);
    350   GNUNET_free(url);
    351   TALER_LOG_DEBUG("Command for QRshow: %s\n",command);
    352   system(command);
    353 
    354   GNUNET_free(command);
    355   GNUNET_free(path);
    356 }
    357 
    358 void
    359 clear_screen(void)
    360 {
    361   char *command;
    362   system(command);
    363   GNUNET_asprintf(&command,
    364                   "dd if=/dev/zero of=%s bs=%d count=%d\n",
    365                   cfg_ca_device,
    366                   DISPLAY_BLOCK_SIZE,
    367                   DISPLAY_COUNT);
    368   GNUNET_free(command);
    369 }
    370 
    371 /**
    372  * Joinn currency and V.F format value string to TALER amount".
    373  *
    374  * @param currency currency string
    375  * @param value_fraction value string format "Value.Fraction"
    376  * @param[out] amount amount to write the result to
    377  * @return #GNUNET_OK if the string is a valid monetary amount specification,
    378  *         #GNUNET_SYSERR if it is invalid.
    379  */
    380 enum GNUNET_GenericReturnValue
    381 join_strings_to_amount(const char *currency,
    382                             const char *value_fraction,
    383                             struct TALER_Amount *amount)
    384 {
    385   enum GNUNET_GenericReturnValue ret;
    386   char *str;
    387   GNUNET_asprintf (&str,
    388                     "%s:%s",
    389                     currency,
    390                     value_fraction);
    391   ret = TALER_string_to_amount(str,
    392                               amount);
    393   GNUNET_free(str);
    394   return ret;
    395 }
    396 
    397 
    398 static void
    399 on_get_config_done (void *cls,
    400                     const struct TALER_BANK_ConfigResponse *vr)
    401 {
    402   TALER_LOG_DEBUG ("Callback of GET /config\n");
    403   (void) cls;
    404   get_config_handle = NULL;
    405 
    406   state_ctx->version_compa = vr->details.ok.version_compa;
    407   state_ctx->currency_compa = strcmp(vr->details.ok.configi.currency,
    408                                      cfg_currency);
    409   if(TALER_BANK_CC_MATCH != state_ctx->currency_compa)
    410   {
    411     GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    412                               "taler-digitizer",
    413                               "CURRENCY",
    414                               "not compatible with bank currency");
    415     state = DIGITIZER_STATE_TERMINAL_ERROR;
    416     GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    417     return;
    418   }
    419   if(-1 == TALER_amount_cmp(&(vr->details.ok.configi.max_wire_transfer_amount),
    420                     &(state_ctx->max_insertion_amount)))
    421   {
    422     GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    423                               "taler-digitizer",
    424                               "BANK_BASE_URL or MAX_DENOMINATION",
    425                               "max denominations not compatible with bank max_wire_transfer_amount");
    426     state = DIGITIZER_STATE_TERMINAL_ERROR;
    427     GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    428     return;
    429   }
    430   state_ctx->bank_max_wire_transfer_amount = vr->details.ok.configi.max_wire_transfer_amount;
    431   if(1 == TALER_amount_cmp(&(vr->details.ok.configi.min_wire_transfer_amount),
    432                             &(state_ctx->min_insertion_amount)))
    433   {
    434     GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    435                               "taler-digitizer",
    436                               "BANK_BASE_URL or MAX_DENOMINATION",
    437                               "min denominations not compatible with bank min_wire_transfer_amount");
    438     state = DIGITIZER_STATE_TERMINAL_ERROR;
    439     GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    440     return;
    441   }
    442   
    443   
    444   state = DIGITIZER_STATE_IDLE;
    445   GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    446   return;
    447 }
    448 
    449 /**
    450  * callback of accounts request
    451  * used in the Idle state
    452  */
    453 static void
    454 on_get_accounts_done(void *cls,
    455                      const struct TALER_BANK_AccountsResponse *vr)
    456 {
    457   TALER_LOG_DEBUG ("Callback of accounts/$USERNAME\n");
    458 
    459   (void) cls;
    460   get_accounts_handle = NULL;
    461   struct TALER_Amount account_max_withdrawal;
    462 
    463   if(0 != strcmp("active",
    464                          vr->details.ok.acc.status))
    465   {
    466     GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    467                               "taler-digitizer",
    468                               "BANK_ACCOUNT",
    469                               "bank account is not in active state");
    470     state = DIGITIZER_STATE_TERMINAL_ERROR;
    471     GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    472     return;
    473   }
    474   TALER_amount_min (&account_max_withdrawal,
    475                   &vr->details.ok.acc.balance.amount,
    476                   &vr->details.ok.acc.debit_threshold);
    477   if(-1 == TALER_amount_cmp (&account_max_withdrawal,
    478                              &state_ctx->max_insertion_amount))
    479   {
    480     TALER_LOG_ERROR("bank account does not have enough balance or whithdrawal limit to withdrawal the biggest denomination");
    481     state = DIGITIZER_STATE_TERMINAL_ERROR;
    482     GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    483     return;
    484   }
    485   TALER_amount_min(&state_ctx->withdrawal_limit,&account_max_withdrawal,&state_ctx->bank_max_wire_transfer_amount);
    486   TALER_amount_min(&state_ctx->withdrawal_limit,&state_ctx->withdrawal_limit,&cfg_user_withdrawallimit);
    487 
    488   
    489 
    490   state = DIGITIZER_STATE_CREATE_QR;
    491   GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    492   return;
    493 }
    494 
    495 /**
    496  * callback of accounts withdrawal request
    497  * used in the QR Create state
    498  */
    499 static void
    500 on_post_accounts_withdrawal_done(void *cls,
    501                                  const struct TALER_BANK_CreateWithdrawalResponse *vr)
    502 {
    503   TALER_LOG_DEBUG ("Callback of accounts/$USERNAME/withdrawal\n");
    504 
    505   (void) cls;
    506   post_accounts_withdrawal_handle = NULL;
    507   
    508   state_ctx->wi.taler_withdraw_uri = GNUNET_strdup(vr->details.ok.wopd.taler_withdraw_uri);
    509   state_ctx->wi.withdrawal_id = GNUNET_strdup(vr->details.ok.wopd.withdrawal_id);
    510 
    511   state = DIGITIZER_STATE_SCAN_QR;
    512   GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    513   return;
    514 }
    515 
    516 /**
    517  * Get withdrawal request for repeated requesting
    518  */
    519 static void
    520 get_withdrawal_task(void *cls);
    521 
    522 /**
    523  * callback of accounts withdrawal request
    524  * used in the QR Create state
    525  */
    526 static void
    527 on_get_withdrawal_status_done(void *cls,
    528                                 const struct TALER_BANK_WithdrawalResponse *vr)
    529 {
    530   TALER_LOG_DEBUG ("Callback of withdrawal/$WITHDRAWAL_ID\n");
    531   struct GNUNET_SCHEDULER_Task *timeout_handle;
    532   timeout_handle = cls;
    533   if(0 == strcasecmp("selected", vr->details.ok.acc.status))
    534   {
    535     GNUNET_SCHEDULER_cancel(timeout_handle);
    536     clear_screen();
    537     state = DIGITIZER_STATE_COUNT_MONEY;
    538     // give context for non terminal errors!
    539     GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    540   }
    541   else
    542   {
    543     struct GNUNET_TIME_Relative poll;
    544     poll = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS,200);
    545     GNUNET_SCHEDULER_add_delayed(poll,get_withdrawal_task,timeout_handle);
    546   }
    547   
    548   return;
    549 }
    550 
    551 /**
    552  * Get withdrawal request for repeated requesting
    553  */
    554 static void
    555 get_withdrawal_task(void *cls)
    556 {
    557   struct GNUNET_SCHEDULER_Task *timeout_handle;
    558   timeout_handle = cls;
    559 
    560   get_withdrawal_handle =
    561   TALER_BANK_get_withdrawal(curl_ctx,
    562                             cfg_bank_base_url,
    563                             state_ctx->wi.withdrawal_id,
    564                             on_get_withdrawal_status_done,
    565                             timeout_handle,
    566                             NULL);
    567   return;
    568 }
    569 
    570 /**
    571  * Timeout when QR is shown
    572  * SCAN_QR_TIMEOUT_SECONDS without been scanned
    573  * used in the ScanQR state
    574  */
    575 static void
    576 timeout_task(void *cls)
    577 {
    578   TALER_LOG_DEBUG ("Timeout of ScanQR\n");
    579 
    580   (void) cls;
    581 
    582   state = DIGITIZER_STATE_CANCEL_WITHDRAWAL;
    583   GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
    584   return;
    585 }
    586 
    587 /**
    588  * @brief Cleanup when no task is scheduled anymore
    589  *
    590  * @param cls closure
    591  */
    592 static void
    593 shutdown_task (void *cls)
    594 {
    595   (void)cls;
    596   gpiod_line_request_release(rl_ca_enable);
    597 
    598   if (NULL != get_withdrawal_handle)
    599   {
    600     TALER_BANK_get_withdrawal_cancel (get_withdrawal_handle);
    601     get_withdrawal_handle = NULL;
    602   }
    603   if (NULL != get_config_handle)
    604   {
    605     TALER_BANK_get_config_cancel (get_config_handle);
    606     get_config_handle = NULL;
    607   }
    608   if (NULL != post_accounts_withdrawal_handle)
    609   {
    610     TALER_BANK_post_withdrawal_create_cancel(post_accounts_withdrawal_handle);
    611     post_accounts_withdrawal_handle = NULL;
    612   }
    613     if (NULL != reschedule_ctx)
    614   {
    615     GNUNET_CURL_gnunet_rc_destroy (reschedule_ctx);
    616     reschedule_ctx = NULL;
    617   }
    618   if (NULL != curl_ctx)
    619   {
    620     GNUNET_CURL_fini (curl_ctx);
    621     curl_ctx = NULL;
    622   }
    623   DIGITIZER_bank_auth_free (cfg_bank_authentication);
    624   GNUNET_free(cfg_bank_authentication);
    625   GNUNET_free(state_ctx);
    626   GNUNET_free(display_ctx);
    627 }
    628 
    629 /**
    630  * Initialising state
    631  * Init: Digitizer config, Bank config, Screen task, Touch task
    632  */
    633 static void Init_state_task(void *cls)
    634 {
    635   TALER_LOG_DEBUG ("INIT state\n");
    636 
    637   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    638 
    639   if (GNUNET_OK !=
    640       GNUNET_CONFIGURATION_get_value_string (cfg,
    641                                              "taler-digitizer",
    642                                              "BANK_BASE_URL",
    643                                              &cfg_bank_base_url))
    644   {
    645     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    646                                "taler-digitizer",
    647                                "BANK_BASE_URL");
    648     global_ret = EXIT_FAILURE;
    649     return;
    650   }
    651   if (GNUNET_OK !=
    652       GNUNET_CONFIGURATION_get_value_string (cfg,
    653                                              "taler-digitizer",
    654                                              "BANK_ACCOUNT",
    655                                              &cfg_bank_account_username))
    656   {
    657     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    658                                "taler-digitizer",
    659                                "BANK_ACCOUNT");
    660     global_ret = EXIT_FAILURE;
    661     return;
    662   }
    663   if (GNUNET_OK !=
    664       DIGITIZER_bank_auth_parse_cfg (cfg,
    665                                      "bank-authentication",
    666                                      cfg_bank_authentication))
    667   {
    668     global_ret = EXIT_FAILURE;
    669     return;
    670   }
    671   if (GNUNET_OK !=
    672       GNUNET_CONFIGURATION_get_value_string (cfg,
    673                                              "taler-digitizer",
    674                                              "EXCHANGE_BASE_URL",
    675                                              &cfg_exchange_base_url))
    676   {
    677     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    678                                "taler-digitizer",
    679                                "EXCHANGE_BASE_URL");
    680     global_ret = EXIT_FAILURE;
    681     return;
    682   }
    683   if (GNUNET_OK !=
    684       GNUNET_CONFIGURATION_get_value_string (cfg,
    685                                              "taler-digitizer",
    686                                              "CURRENCY",
    687                                              &cfg_currency))
    688   {
    689     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    690                                "taler-digitizer",
    691                                "CURRENCY");
    692     global_ret = EXIT_FAILURE;
    693     return;
    694   }
    695   if(GNUNET_OK != TALER_check_currency(cfg_currency))
    696   {
    697     GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    698                               "taler-digitizer",
    699                               "CURRENCY",
    700                             "malformed currency");
    701     global_ret = EXIT_FAILURE;
    702     return;                   
    703   }
    704   char *limit_value;
    705   if (GNUNET_OK !=
    706     GNUNET_CONFIGURATION_get_value_string (cfg,
    707                                           "taler-digitizer",
    708                                           "USER_WITHDRAWALLIMIT",
    709                                           &limit_value))
    710   {
    711     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    712                               "taler-digitizer",
    713                                "USER_WITHDRAWALLIMIT");
    714     GNUNET_free(limit_value);
    715     global_ret = EXIT_FAILURE;
    716     return;
    717   }
    718   if (GNUNET_OK !=
    719   join_strings_to_amount(cfg_currency,
    720                                 limit_value,
    721                                 &cfg_user_withdrawallimit))
    722   {
    723     GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    724                               "taler-digitizer",
    725                               "USER_WITHDRAWALLIMIT",
    726                               "malformed denomination");
    727   GNUNET_free(limit_value);
    728   global_ret = EXIT_FAILURE;
    729   return; 
    730   }
    731   GNUNET_free(limit_value);
    732 
    733   unsigned long long user_withdrawal_period_number;
    734   if (GNUNET_OK !=
    735       GNUNET_CONFIGURATION_get_value_number (cfg,
    736                                              "taler-digitizer",
    737                                              "USER_WITHDRAWAL_PERIOD",
    738                                              &user_withdrawal_period_number))
    739   {
    740     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    741                                "taler-digitizer",
    742                                "USER_WITHDRAWAL_PERIOD");
    743     global_ret = EXIT_FAILURE;
    744     return;
    745   }
    746   cfg_user_withdrawal_period = GNUNET_TIME_relative_multiply (
    747     DIGITIZER_PERSON_WITHDRAWAL_PERIOD_TIME_UNIT,
    748     user_withdrawal_period_number);
    749 
    750   cfg_kyc_functionality = GNUNET_CONFIGURATION_get_value_yesno (cfg,
    751                                                                  "taler-digitizer",
    752                                                                  "KYC_FUNCTIONALITY");
    753   if (GNUNET_SYSERR == cfg_kyc_functionality)
    754   {
    755     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    756                                "taler-digitizer",
    757                                "KYC_FUNCTIONALITY");
    758     global_ret = EXIT_FAILURE;
    759     return;
    760   }
    761   if (GNUNET_OK !=
    762       GNUNET_CONFIGURATION_get_value_filename (cfg,
    763                                              "taler-digitizer",
    764                                              "FRAMEBUFFER_DEVICE",
    765                                              &cfg_framebuffer_device))
    766   {
    767     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    768                                "taler-digitizer",
    769                                "FRAMEBUFFER_DEVICE");
    770     global_ret = EXIT_FAILURE;
    771     return;
    772   }
    773   if (GNUNET_OK !=
    774       GNUNET_CONFIGURATION_get_value_filename (cfg,
    775                                              "taler-digitizer",
    776                                              "FRAMEBUFFER_BACKLIGHT",
    777                                              &cfg_framebuffer_backlight))
    778   {
    779     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    780                                "taler-digitizer",
    781                                "FRAMEBUFFER_BACKLIGHT");
    782     global_ret = EXIT_FAILURE;
    783     return;
    784   }
    785   cfg_ca_enable = GNUNET_CONFIGURATION_get_value_yesno (cfg,
    786                                                         "coin-acceptor",
    787                                                         "ENABLE");
    788   cfg_ba_enable = GNUNET_CONFIGURATION_get_value_yesno (cfg,
    789                                                         "bill-acceptor",
    790                                                         "ENABLE");
    791   if(GNUNET_OK != cfg_ba_enable)
    792   {
    793     cfg_ba_enable = GNUNET_NO;
    794     join_strings_to_amount(cfg_currency,
    795                            "0",
    796                            &cfg_ba_max_denomination);
    797     join_strings_to_amount(cfg_currency,
    798                            "0",
    799                            &cfg_ba_min_denomination);
    800     if(GNUNET_OK != cfg_ca_enable)
    801     {
    802       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    803                                   "coin-acceptor or bill-acceptor",
    804                                   "ENABLE");
    805       global_ret = EXIT_FAILURE;
    806       return;
    807     }
    808   }
    809   else
    810   {
    811     if (GNUNET_OK !=
    812       GNUNET_CONFIGURATION_get_value_filename (cfg,
    813                                              "bill-acceptor",
    814                                              "DEVICE",
    815                                              &cfg_ba_device))
    816     {
    817       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    818                                 "bill-acceptor",
    819                                 "DEVICE");
    820       global_ret = EXIT_FAILURE;
    821       return;
    822     }
    823     char *denomination_value;
    824     if (GNUNET_OK !=
    825       GNUNET_CONFIGURATION_get_value_string (cfg,
    826                                              "bill-acceptor",
    827                                              "MAX_DENOMINATION",
    828                                              &denomination_value))
    829     {
    830       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    831                                 "bill-acceptor",
    832                                 "MAX_DENOMINATION");
    833       GNUNET_free(denomination_value);
    834       global_ret = EXIT_FAILURE;
    835       return;
    836     }
    837     if (GNUNET_OK !=
    838         join_strings_to_amount(cfg_currency,
    839                                  denomination_value,
    840                                  &cfg_ba_max_denomination))
    841     {
    842       GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    843                                 "bill-acceptor",
    844                                 "MAX_DENOMINATION",
    845                                 "malformed denomination");
    846     GNUNET_free(denomination_value);
    847     global_ret = EXIT_FAILURE;
    848     return; 
    849     }
    850     GNUNET_free(denomination_value);
    851     if (GNUNET_OK !=
    852       GNUNET_CONFIGURATION_get_value_string (cfg,
    853                                              "bill-acceptor",
    854                                              "MIN_DENOMINATION",
    855                                              &denomination_value))
    856     {
    857       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    858                                 "bill-acceptor",
    859                                 "MIN_DENOMINATION");
    860       GNUNET_free(denomination_value);
    861       global_ret = EXIT_FAILURE;
    862       return;
    863     }
    864     if (GNUNET_OK !=
    865       join_strings_to_amount(cfg_currency,
    866                                  denomination_value,
    867                                  &cfg_ba_min_denomination))
    868     {
    869       GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    870                                 "bill-acceptor",
    871                                 "MIN_DENOMINATION",
    872                                 "malformed denomination");
    873     GNUNET_free(denomination_value);
    874     global_ret = EXIT_FAILURE;
    875     return; 
    876     }
    877     GNUNET_free(denomination_value);
    878   }
    879   if(GNUNET_OK != cfg_ca_enable)
    880   {
    881     cfg_ca_enable = GNUNET_NO;
    882     join_strings_to_amount(cfg_currency,
    883                            "0",
    884                            &cfg_ca_max_denomination);
    885     join_strings_to_amount(cfg_currency,
    886                            "0",
    887                            &cfg_ca_min_denomination);
    888   }
    889   else
    890   {
    891     char *denomination_value;
    892     if (GNUNET_OK !=
    893       GNUNET_CONFIGURATION_get_value_string (cfg,
    894                                              "coin-acceptor",
    895                                              "MAX_DENOMINATION",
    896                                              &denomination_value))
    897     {
    898       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    899                                 "coin-acceptor",
    900                                 "MAX_DENOMINATION");
    901       GNUNET_free(denomination_value);
    902       global_ret = EXIT_FAILURE;
    903       return;
    904     }
    905     if (GNUNET_OK !=
    906     join_strings_to_amount(cfg_currency,
    907                                  denomination_value,
    908                                  &cfg_ca_max_denomination))
    909     {
    910       GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    911                                 "coin-acceptor",
    912                                 "MAX_DENOMINATION",
    913                                 "malformed denomination");
    914     GNUNET_free(denomination_value);
    915     global_ret = EXIT_FAILURE;
    916     return; 
    917     }
    918     if (GNUNET_OK !=
    919       GNUNET_CONFIGURATION_get_value_string (cfg,
    920                                              "coin-acceptor",
    921                                              "MIN_DENOMINATION",
    922                                              &denomination_value))
    923     {
    924       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    925                                 "coin-acceptor",
    926                                 "MIN_DENOMINATION");
    927       GNUNET_free(denomination_value);
    928       global_ret = EXIT_FAILURE;
    929       return;
    930     }
    931     if (GNUNET_OK !=
    932     join_strings_to_amount(cfg_currency,
    933                                  denomination_value,
    934                                  &cfg_ca_min_denomination))
    935     {
    936       GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
    937                                 "coin-acceptor",
    938                                 "MIN_DENOMINATION",
    939                                 "malformed denomination");
    940     GNUNET_free(denomination_value);
    941     global_ret = EXIT_FAILURE;
    942     return; 
    943     }
    944     GNUNET_free(denomination_value);
    945     if (GNUNET_OK !=
    946       GNUNET_CONFIGURATION_get_value_filename (cfg,
    947                                              "coin-acceptor",
    948                                              "DEVICE",
    949                                              &cfg_ca_device))
    950     {
    951       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    952                                 "coin-acceptor",
    953                                 "DEVICE");
    954       global_ret = EXIT_FAILURE;
    955       return;
    956     }
    957     if (GNUNET_OK !=
    958       GNUNET_CONFIGURATION_get_value_number (cfg,
    959                                              "coin-acceptor",
    960                                              "ENABLE_PIN",
    961                                              &cfg_ca_en_pin))
    962     {
    963       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    964                                 "coin-acceptor",
    965                                 "ENABLE_PIN");
    966       global_ret = EXIT_FAILURE;
    967       return;
    968     }
    969     if (GNUNET_OK !=
    970       GNUNET_CONFIGURATION_get_value_number (cfg,
    971                                              "coin-acceptor",
    972                                              "UART_RX_PIN",
    973                                              &cfg_ca_rx_pin))
    974     {
    975       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    976                                 "coin-acceptor",
    977                                 "UART_RX_PIN");
    978       global_ret = EXIT_FAILURE;
    979       return;
    980     }
    981     //CA Init
    982     gpio_settings = gpiod_make_settings(GPIOD_LINE_DIRECTION_OUTPUT,
    983                                         GPIOD_LINE_BIAS_PULL_UP,
    984                                         GPIOD_LINE_DRIVE_OPEN_DRAIN,1);
    985     if (NULL == gpio_settings)
    986     {
    987       TALER_LOG_ERROR("GPIO Init failed");
    988       gpiod_line_settings_free(gpio_settings);
    989       global_ret = EXIT_FAILURE;
    990       return;
    991     }
    992     rl_ca_enable = gpiod_make_line_request(cfg_ca_device,
    993                                           cfg_ca_en_pin,
    994                                           gpio_settings);
    995     if (NULL == rl_ca_enable)
    996     {
    997       TALER_LOG_ERROR("GPIO Init failed");
    998       gpiod_line_request_release(rl_ca_enable);
    999       global_ret = EXIT_FAILURE;
   1000       return; 
   1001     }
   1002     if(0 != gpiod_line_request_set_value(rl_ca_enable,
   1003                                       (unsigned int)cfg_ca_en_pin,
   1004                                       GPIOD_LINE_VALUE_INACTIVE))
   1005   {
   1006     TALER_LOG_ERROR("failed to set GPIO active");
   1007     global_ret = EXIT_FAILURE;
   1008     return;
   1009   }
   1010   }
   1011 
   1012   state_ctx->min_insertion_amount = 
   1013   ((1 == TALER_amount_cmp(&cfg_ba_min_denomination,
   1014                           &cfg_ca_min_denomination))?
   1015   cfg_ca_min_denomination : cfg_ba_min_denomination);
   1016 
   1017   if(TALER_AAR_INVALID_NEGATIVE_RESULT == TALER_amount_add(&state_ctx->max_insertion_amount,
   1018                                                            &cfg_ba_min_denomination,
   1019                                                            &cfg_ca_min_denomination))
   1020   {
   1021     GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
   1022                                 "coin-acceptor",
   1023                                 "XXX_DENOMINATION",
   1024                                 "could not determine min insertion amount");
   1025     global_ret = EXIT_FAILURE;
   1026     return; 
   1027   }
   1028 
   1029   //Screen INIT
   1030 
   1031   //Touch INIT
   1032 
   1033   //BA Init
   1034 
   1035 
   1036   curl_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   1037                                       &reschedule_ctx);
   1038   reschedule_ctx = GNUNET_CURL_gnunet_rc_create (curl_ctx);
   1039 
   1040   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
   1041                                 NULL);
   1042                       
   1043   get_config_handle = TALER_BANK_get_config (curl_ctx,
   1044                                             cfg_bank_base_url,
   1045                                             &on_get_config_done,
   1046                                             NULL);
   1047 }
   1048 
   1049 /**
   1050  * Terminal Error state
   1051  * When error accures due to config issues, or unrecoverable errors
   1052  */
   1053 static void TerminalError_state_task(void *cls)
   1054 {
   1055   (void)cls;
   1056   TALER_LOG_DEBUG ("start TerminalError state task\n");
   1057   global_ret = EXIT_FAILURE;
   1058   GNUNET_SCHEDULER_shutdown();
   1059 }
   1060 
   1061 /**
   1062  * Idle state
   1063  * prepare for new withdrawal process and
   1064  * check for start signal from UI
   1065  */
   1066 static void Idle_state_task(void *cls)
   1067 {
   1068   TALER_LOG_DEBUG("Idle state\n");
   1069   clear_screen();
   1070   (void)cls;
   1071   TALER_amount_set_zero(cfg_currency,&state_ctx->digitizer_user_balance);
   1072   get_accounts_handle = TALER_BANK_get_accounts (curl_ctx,
   1073                                                 cfg_bank_base_url,
   1074                                                 cfg_bank_account_username,
   1075                                                 cfg_bank_authentication,
   1076                                                 &on_get_accounts_done,
   1077                                                 NULL);
   1078 }
   1079 
   1080 /**
   1081  * Create QR state
   1082  * create QR code with a new WOPID
   1083  * check for cancle from UI
   1084  */
   1085 static void CreateQR_state_task(void *cls)
   1086 {
   1087   TALER_LOG_DEBUG("CreateQR state\n");
   1088   (void)cls;
   1089   struct TALER_BANK_AccountCreateWithdrawalRequest *acwr;
   1090   acwr = GNUNET_new(struct TALER_BANK_AccountCreateWithdrawalRequest);
   1091   acwr->no_amount_to_wallet = true;
   1092   
   1093   post_accounts_withdrawal_handle = 
   1094   TALER_BANK_post_accounts_withdrawal_create (curl_ctx,
   1095                                               cfg_bank_base_url,
   1096                                               cfg_bank_account_username,
   1097                                               cfg_bank_authentication);
   1098   if(GNUNET_OK !=
   1099     TALER_BANK_post_accounts_withdrawal(post_accounts_withdrawal_handle,
   1100                                         acwr,
   1101                                         on_post_accounts_withdrawal_done,
   1102                                         NULL))
   1103   {
   1104     TALER_LOG_ERROR("TALER_BANK_post_accounts_withdrawal canceled\n");
   1105     state = DIGITIZER_STATE_CANCEL_WITHDRAWAL;
   1106     // give context for non terminal errors!
   1107     GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
   1108   }
   1109   GNUNET_free(acwr);
   1110   return;
   1111 }
   1112 
   1113 /**
   1114  * Show QR state
   1115  * show QR code on display
   1116  * check for cancle from UI
   1117  */
   1118 static void ScanQR_state_task(void *cls)
   1119 {
   1120   TALER_LOG_DEBUG ("ScanQR state\n");
   1121   (void)cls;
   1122   
   1123   struct GNUNET_SCHEDULER_Task *timeout_handle;
   1124   struct GNUNET_TIME_Relative delay;
   1125   struct GNUNET_TIME_Relative poll;
   1126 
   1127   delay = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,SCAN_QR_TIMEOUT_SECONDS);
   1128   poll = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS,200);
   1129   timeout_handle = GNUNET_SCHEDULER_add_delayed(delay,timeout_task,NULL);
   1130   start_qr_show(SCAN_QR_TIMEOUT_SECONDS);
   1131 
   1132   //char *buf;
   1133   //GNUNET_asprintf(&buf,"%d",SCAN_QR_TIMEOUT_SECONDS*1000);
   1134 
   1135   GNUNET_SCHEDULER_add_delayed(poll,get_withdrawal_task,timeout_handle);
   1136   return;
   1137 }
   1138 
   1139 static void CountMoney_state_task(void *cls)
   1140 {
   1141   char* balance_str;
   1142   struct TALER_Amount *temp_amount;
   1143   temp_amount = GNUNET_new(struct TALER_Amount);
   1144   balance_str = TALER_amount_to_string(&state_ctx->digitizer_user_balance);
   1145   TALER_LOG_DEBUG ("CountMoney: %s\n",balance_str);
   1146   GNUNET_free(balance_str);
   1147   (void)cls;
   1148   if(0 != gpiod_line_request_set_value(rl_ca_enable,
   1149                                       (unsigned int)cfg_ca_en_pin,
   1150                                       GPIOD_LINE_VALUE_INACTIVE))
   1151     {
   1152       GNUNET_free(temp_amount);
   1153       if(TALER_amount_is_zero(&state_ctx->digitizer_user_balance))
   1154       {
   1155         TALER_LOG_ERROR("failed to set GPIO inactive");
   1156         state = DIGITIZER_STATE_CANCEL_WITHDRAWAL;
   1157         GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
   1158       }
   1159       else
   1160       {
   1161         TALER_LOG_ERROR("failed to set GPIO active");
   1162         state = DIGITIZER_STATE_CASHING_UP;
   1163         GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
   1164       }  
   1165     }
   1166 
   1167   TALER_amount_subtract(temp_amount,
   1168                         &state_ctx->withdrawal_limit,
   1169                         &state_ctx->digitizer_user_balance);
   1170   if(-1 == TALER_amount_cmp(temp_amount,
   1171                             &state_ctx->max_insertion_amount))
   1172   {
   1173     GNUNET_free(temp_amount);
   1174     TALER_LOG_INFO("withdrawal limit reached");
   1175     state = DIGITIZER_STATE_CASHING_UP;
   1176     GNUNET_SCHEDULER_add_now(state_controller_task,NULL);  
   1177   }else
   1178   {
   1179     GNUNET_free(temp_amount);
   1180     if(0 != gpiod_line_request_set_value(rl_ca_enable,
   1181                                       (unsigned int)cfg_ca_en_pin,
   1182                                       GPIOD_LINE_VALUE_ACTIVE))
   1183     {
   1184       TALER_LOG_ERROR("failed to set GPIO active");
   1185       state = DIGITIZER_STATE_CASHING_UP;
   1186       GNUNET_SCHEDULER_add_now(state_controller_task,NULL);  
   1187     }
   1188     state = DIGITIZER_STATE_ACCEPT_CASH;
   1189     GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 
   1190   }
   1191 }
   1192 
   1193 static void AcceptCash_state_task(void *cls)
   1194 {
   1195   (void)cls;
   1196   TALER_LOG_DEBUG("AcceptingCash state");
   1197   state = DIGITIZER_STATE_CASHING_UP;
   1198   GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 
   1199 }
   1200 
   1201 /**
   1202  * Switch between State tasks
   1203  */
   1204 static void state_controller_task(void *cls)
   1205 {
   1206   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   1207 
   1208   switch (state)
   1209   {
   1210   case DIGITIZER_STATE_INIT:
   1211   {
   1212     GNUNET_SCHEDULER_add_now(Init_state_task,cfg);
   1213     return;
   1214   }
   1215   case DIGITIZER_STATE_IDLE:
   1216   {
   1217     GNUNET_SCHEDULER_add_now(Idle_state_task,NULL);
   1218     return;
   1219   }
   1220   case DIGITIZER_STATE_TERMINAL_ERROR:
   1221   {
   1222     GNUNET_SCHEDULER_add_now(TerminalError_state_task,NULL);
   1223     return;
   1224   }
   1225     case DIGITIZER_STATE_CREATE_QR:
   1226   {
   1227     GNUNET_SCHEDULER_add_now(CreateQR_state_task,NULL);
   1228     return;
   1229   }
   1230   case DIGITIZER_STATE_SCAN_QR:
   1231   {
   1232     GNUNET_SCHEDULER_add_now(ScanQR_state_task,NULL);
   1233     return;
   1234   }
   1235   case DIGITIZER_STATE_COUNT_MONEY:
   1236   {
   1237     GNUNET_SCHEDULER_add_now(CountMoney_state_task,NULL);
   1238     return;
   1239   }
   1240   case DIGITIZER_STATE_ACCEPT_CASH:
   1241   {
   1242     GNUNET_SCHEDULER_add_now(AcceptCash_state_task,NULL);
   1243     return;
   1244   }
   1245 
   1246   default:
   1247   {
   1248     TALER_LOG_ERROR("Gost-State in state machine\n");
   1249     global_ret = EXIT_FAILURE;
   1250     GNUNET_SCHEDULER_shutdown ();
   1251   }
   1252     
   1253   }
   1254 }
   1255 
   1256 
   1257 /**
   1258  * @brief Start the application
   1259  *
   1260  * @param cls closure
   1261  * @param args arguments left
   1262  * @param cfgfile config file name
   1263  * @param cfg handle for the configuration
   1264  */
   1265 static void
   1266 run (void *cls,
   1267      char *const *args,
   1268      const char *cfgfile,
   1269      const struct GNUNET_CONFIGURATION_Handle *cfg)
   1270 {
   1271   (void) cls;
   1272   (void) args;
   1273   (void) cfgfile;
   1274 
   1275   state = DIGITIZER_STATE_INIT;
   1276   state_ctx = GNUNET_new(struct DIGITIZER_StateContext);
   1277   display_ctx = GNUNET_new(struct DIGITIZER_DisplayContext);
   1278   cfg_bank_authentication = GNUNET_new(struct DIGITIZER_BankAuthenticationData);
   1279 
   1280   GNUNET_SCHEDULER_add_now(state_controller_task,(void *) cfg);
   1281 
   1282   return;
   1283 }
   1284 
   1285 
   1286 int
   1287 main (int argc,
   1288       char *const *argv)
   1289 {
   1290   int ret;
   1291 
   1292   struct GNUNET_GETOPT_CommandLineOption options[] = {
   1293     GNUNET_GETOPT_option_flag ('d',
   1294                                "enable-diagnostics",
   1295                                "enable diagnostics for debuging",
   1296                                &enable_diagnostics),
   1297     GNUNET_GETOPT_OPTION_END
   1298   };
   1299 
   1300   ret = GNUNET_PROGRAM_run (TALER_DIGITIZER_project_data (),
   1301                             argc,
   1302                             argv,
   1303                             "taler-digitizer",
   1304                             "This is an application for the Cash Digitizer."
   1305                             " It accepts cash and transfers it to the Taler Wallet.\n",
   1306                             options,
   1307                             &run,
   1308                             NULL);
   1309 
   1310   if (GNUNET_NO == ret)
   1311     return 0;
   1312   if (GNUNET_OK != ret)
   1313     return 1;
   1314   return global_ret;
   1315 }