taler-mdb

GNU Taler Extensions and Integrations
Log | Files | Refs | Submodules | README | LICENSE

taler-mdb.c (103852B)


      1 /*
      2  This file is part of TALER
      3  Copyright (C) 2019, 2020, 2022, 2024 Taler Systems SA
      4 
      5  TALER is free software; you can redistribute it and/or modify it under the
      6  terms of the GNU 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
     11 FITNESS FOR
     12  A PARTICULAR PURPOSE.  See the GNU General Public License for more
     13 details.
     14 
     15  You should have received a copy of the GNU General Public License
     16 along with
     17  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file taler-mdb.c
     21  * @brief runs the payment logic for a Taler-enabled snack machine
     22  * @author Marco Boss
     23  * @author Christian Grothoff
     24  * @author Dominik Hofer
     25  */
     26 #include "config.h"
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <signal.h>
     31 #include <unistd.h>
     32 #include <sys/socket.h>
     33 #if HAVE_SYS_UN_H
     34 #include <sys/un.h>
     35 #endif
     36 #if HAVE_NETINET_IN_H
     37 #include <netinet/in.h>
     38 #endif
     39 #if HAVE_NETINET_IP_H
     40 #include <netinet/ip.h>         /* superset of previous */
     41 #endif
     42 #include <sys/stat.h>
     43 #include <sys/types.h>
     44 #include <errno.h>
     45 #include <termios.h>
     46 #include <nfc/nfc.h>
     47 #include <microhttpd.h>
     48 #include <gnunet/gnunet_util_lib.h>
     49 #include <gnunet/gnunet_json_lib.h>
     50 #include <taler/taler_json_lib.h>
     51 #include <taler/taler_merchant_service.h>
     52 #if HAVE_QRENCODE_H
     53 #include <qrencode.h>
     54 #endif
     55 #include <sys/mman.h>
     56 #include <sys/ioctl.h>
     57 #include <fcntl.h>
     58 /* for adafruit pitft display */
     59 #include <linux/fb.h>
     60 #include "taler_mdb_util.h"
     61 
     62 #ifndef EXIT_NOTCONFIGURED
     63 #define EXIT_NOTCONFIGURED 6
     64 #endif
     65 
     66 
     67 /* Constants */
     68 #define MAX_SIZE_RX_BUFFER 256
     69 
     70 /* Constants */
     71 #define MAX_SIZE_TX_BUFFER 256
     72 
     73 /**
     74  * Disable i18n support.
     75  */
     76 #define _(s) (s)
     77 
     78 #define BACKEND_POLL_TIMEOUT GNUNET_TIME_relative_multiply ( \
     79           GNUNET_TIME_UNIT_SECONDS, 30)
     80 
     81 /**
     82  * Set payment deadline below what will work with the snack machine.
     83  */
     84 #define PAY_TIMEOUT GNUNET_TIME_relative_multiply ( \
     85           GNUNET_TIME_UNIT_MINUTES, 2)
     86 
     87 /**
     88  * How long to show a transient error.
     89  */
     90 #define ERR_DELAY GNUNET_TIME_relative_multiply ( \
     91           GNUNET_TIME_UNIT_SECONDS, 30)
     92 
     93 /**
     94  * How long could it take at most for us to notify the Taler merchant
     95  * backend to grant a refund to a user if dispensing the product
     96  * failed? (Very conservative value here, for vending machines brewing
     97  * coffee or something complex that could fail.)
     98  */
     99 #define MAX_REFUND_DELAY GNUNET_TIME_relative_multiply ( \
    100           GNUNET_TIME_UNIT_MINUTES, 5)
    101 
    102 
    103 #define NFC_FAILURE_RETRY_FREQ GNUNET_TIME_UNIT_MINUTES
    104 
    105 #define NFC_NOT_FOUND_RETRY_FREQ GNUNET_TIME_UNIT_SECONDS
    106 
    107 /**
    108  * How long do we wait at most for an ACK from MDB?
    109  */
    110 #define MAX_ACK_LATENCY GNUNET_TIME_UNIT_SECONDS
    111 
    112 /**
    113  * Timeout in milliseconds for libnfc operations.
    114  */
    115 #define NFC_TIMEOUT 500
    116 
    117 #define MAX_HTTP_RETRY_FREQ GNUNET_TIME_relative_multiply ( \
    118           GNUNET_TIME_UNIT_MILLISECONDS, 500)
    119 
    120 #define MAX_HTTP_RETRY_FREQ GNUNET_TIME_relative_multiply ( \
    121           GNUNET_TIME_UNIT_MILLISECONDS, 500)
    122 
    123 /**
    124  * Code returned by libnfc in case of success.
    125  */
    126 #define APDU_SUCCESS "\x90\x00"
    127 
    128 /**
    129  * Code returned by libnfc in case Taler wallet is not installed.
    130  */
    131 #define APDU_NOT_FOUND "\x6a\x82"
    132 
    133 /* upper and lower bounds for mifare targets uid length */
    134 /**
    135   * Upper length of the uid for a valid MIFARE target
    136   */
    137 #define UID_LEN_UPPER 7
    138 
    139 /**
    140   * Lower length of the uid for a valid MIFARE target
    141   */
    142 #define UID_LEN_LOWER 4
    143 
    144 /* Commands for communication via MDB/ICP */
    145 /* VMC commands */
    146 #define VMC_CMD_START 0x02
    147 #define VMC_CMD_END 0x03
    148 #define VMC_CMD_RESET 0x10
    149 
    150 /**
    151  * Acknowledgement
    152  */
    153 #define VMC_ACKN 0x00
    154 
    155 /**
    156  * Request for configuration.
    157  */
    158 #define VMC_CONF 0x11
    159 #define VMC_READER_CONF 0x00
    160 #define VMC_SETUP_MAX_MIN_PRICES 0x01
    161 
    162 /**
    163  * Machine is polling for something.
    164  */
    165 #define VMC_POLL 0x12
    166 
    167 /**
    168  * Vending, with sub-command.
    169  */
    170 #define VMC_VEND 0x13
    171 #define VMC_VEND_REQUEST 0x00
    172 #define VMC_VEND_CANCEL 0x01
    173 #define VMC_VEND_SUCCESS 0x02
    174 #define VMC_VEND_FAILURE 0x03
    175 #define VMC_VEND_SESSION_COMPLETE 0x04
    176 
    177 /**
    178  * VMC Revalue Request
    179  */
    180 #define VMC_REVALUE 0x15
    181 #define VMC_REVALUE_REQUEST 0x00
    182 #define VMC_REVALUE_LIMIT_REQUEST 0x01
    183 /**
    184  * Commands for the reader (our device).
    185  */
    186 #define VMC_READER 0x14
    187 #define VMC_READER_DISABLE 0x00
    188 #define VMC_READER_ENABLE 0x01
    189 #define VMC_READER_CANCEL 0x02
    190 
    191 #define VMC_REQUEST_ID 0x17
    192 
    193 /**
    194  * Out of sequence.
    195  */
    196 #define VMC_OOSQ 0xB0
    197 
    198 /**
    199  * Request to retransmit last command.
    200  */
    201 #define VMC_RETR 0xAA
    202 
    203 /* Reader commands */
    204 
    205 /* Reader Not Acknowledge */
    206 #define READER_NACK "FF"
    207 
    208 /* Config Data */
    209 /* Refer to the mdb interface specifications v4.2 p.288 */
    210 #define READER_CONFIG "01"
    211 #define READER_FEATURE_LEVEL "01"
    212 #define READER_COUNTRYCODE "0972"
    213 #define READER_SCALE_FACTOR "0A"
    214 #define READER_DECIMAL_PLACES "02"
    215 #define READER_MAX_RESPONSE_TIME "07"
    216 #define READER_MISC_OPTIONS "0D"
    217 
    218 /* Session Commands */
    219 /* Refer to the mdb interface specifications v4.2 p.131 */
    220 #define READER_BEGIN_SESSION "03"
    221 #define READER_FUNDS_AVAILABLE "000A"
    222 #define READER_END_SESSION "07"
    223 
    224 /* Vend Commands */
    225 /* Refer to the mdb interface specifications v4.2 p.134 */
    226 #define READER_VEND_APPROVE "05"
    227 #define READER_VEND_AMOUNT "FFFE"
    228 #define READER_VEND_DENIED "06"
    229 
    230 /* Revalue */
    231 #define READER_REVALUE_APPROVED "0D"
    232 #define READER_REVALUE_APPROVED "0D"
    233 #define READER_REVALUE_LIMIT "0F"
    234 #define READER_REVALUE_LIMIT_AMOUNT "FFFE"
    235 
    236 /* Cancelled Command */
    237 #define READER_CANCELLED "08"
    238 
    239 /* Display Request for Sold Out product */
    240 #define READER_DISPLAY_REQUEST "02"
    241 #define READER_DISPLAY_REQUEST_TIME "32"
    242 #define READER_DISPLAY_SOLD_OUT \
    243         "202020202020202050726f6475637420736f6c64206f75742020202020202020"
    244 #define READER_DISPLAY_INTERNAL_ERROR \
    245         "202020496e7465726e616c204572726f72202d2054727920416761696e202020i"
    246 #define READER_DISPLAY_BACKEND_NOT_REACHABLE \
    247         "20202020204261636b656e64206e6f7420726561636861626c65202020202020"
    248 
    249 /* Unused reader commands */
    250 #define READER_SESSION_CANCEL_REQUEST "04"
    251 #define READER_REVALUE_DENIED "0E"
    252 
    253 /**
    254  * How long are we willing to wait for MDB during
    255  * shutdown?
    256  */
    257 #define SHUTDOWN_MDB_TIMEOUT GNUNET_TIME_relative_multiply ( \
    258           GNUNET_TIME_UNIT_MILLISECONDS, 100)
    259 
    260 /**
    261  * Datatype for mdb subcommands and data
    262  */
    263 struct MdbBlock
    264 {
    265   /**
    266    * Data containing an mdb command or the data of an mdb command
    267    */
    268   uint8_t *bin;
    269 
    270   /**
    271    * Size of the data referenced by *bin
    272    */
    273   size_t bin_size;
    274 };
    275 
    276 
    277 /**
    278  * Datatype for mdb command
    279  */
    280 struct MdbCommand
    281 {
    282   /**
    283    * Name of the command for the logging
    284    */
    285   const char *name;
    286 
    287   /**
    288    * Data block containing the information about the mdb command
    289    */
    290   struct MdbBlock cmd;
    291 
    292   /**
    293    * Data block containing the information about the mdb command data
    294    */
    295   struct MdbBlock data;
    296 };
    297 
    298 
    299 /**
    300  * Struct holding the information for a product to sell
    301  */
    302 struct Product
    303 {
    304   /**
    305    * The price for the product
    306    */
    307   struct TALER_Amount price;
    308 
    309   /**
    310    * Description (or name) of the product
    311    */
    312   char *description;
    313 
    314   /**
    315    * Authorization to header to use for this @e instance.
    316    */
    317   char *auth_header;
    318 
    319   /**
    320    * Which instance should be used for billing?  Full URL, replaces
    321    * the default base URL for orders involving this product.  NULL if
    322    * we should use the #backend_base_url.
    323    */
    324   char *instance;
    325 
    326   /**
    327    * Preview image to embed in the contract, NULL for
    328    * no preview. Already base64 encoded.
    329    */
    330   char *preview;
    331 
    332   /**
    333    * Number of the product in the vending machine
    334    */
    335   unsigned long long number;
    336 
    337   /**
    338    * Set to #GNUNET_YES if this product was found
    339    * to have been sold out (VEND failure).
    340    */
    341   bool sold_out;
    342 
    343   /**
    344    * Key for the product (optional, needed to test the application without vending machine)
    345    */
    346   char key;
    347 
    348 };
    349 
    350 
    351 /**
    352  * Handle for a payment
    353  */
    354 struct PaymentActivity
    355 {
    356 
    357   /**
    358    * Curl context for communication with taler backend
    359    */
    360   struct GNUNET_CURL_Context *ctx;
    361 
    362   /**
    363    * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
    364    */
    365   struct GNUNET_CURL_RescheduleContext *rc;
    366 
    367   /**
    368    * Handle to a POST /orders operation
    369    */
    370   struct TALER_MERCHANT_PostPrivateOrdersHandle *po;
    371 
    372   /**
    373    * Handle for a GET /private/orders/$ID operation.
    374    */
    375   struct TALER_MERCHANT_GetPrivateOrderHandle *ogh;
    376 
    377   /**
    378    * The product being sold.
    379    */
    380   struct Product *product;
    381 
    382   /**
    383    * Base URL for merchant interactions for this pa.
    384    */
    385   const char *base_url;
    386 
    387   /**
    388    * Order ID for pending order
    389    */
    390   char *order_id;
    391 
    392   /**
    393    * URI needed to pay the pending order
    394    */
    395   char *taler_pay_uri;
    396 
    397   /**
    398    * NFC device
    399    */
    400   nfc_device *pnd;
    401 
    402   /**
    403    * Target to send the data via NFC
    404    */
    405   nfc_target nt;
    406 
    407   /**
    408    * Current task running
    409    */
    410   struct GNUNET_SCHEDULER_Task *task;
    411 
    412   /**
    413    * Tasks delayed
    414    */
    415   struct GNUNET_SCHEDULER_Task *delay_task;
    416 
    417   /**
    418    * Payment checking delayed task
    419    */
    420   struct GNUNET_SCHEDULER_Task *delay_pay_task;
    421 
    422   /**
    423    * What is the price the user is paying?
    424    */
    425   struct TALER_Amount amount;
    426 
    427   /**
    428    * Handle for our attempt to delete an ongoing order.
    429    */
    430   struct TALER_MERCHANT_DeletePrivateOrderHandle *odh;
    431 
    432   /**
    433    * Member to see if the wallet already received a uri
    434    * If true, tunneling can be offered to the wallet.
    435    */
    436   bool wallet_has_uri;
    437 
    438   /**
    439    * Set to true once the product has been paid
    440    * (and we are in the process of yielding the product).
    441    */
    442   bool paid;
    443 };
    444 
    445 
    446 /**
    447  * Data structures associated with the MDB.
    448  */
    449 struct MdbHandle
    450 {
    451 
    452   /**
    453    * Buffer to save the received data from UART
    454    */
    455   uint8_t rxBuffer[MAX_SIZE_RX_BUFFER];
    456 
    457   /**
    458    * Buffer to save the data to send via UART
    459    */
    460   uint8_t txBuffer[MAX_SIZE_TX_BUFFER];
    461 
    462   /**
    463    * Reference to scheduler task to read from UART
    464    */
    465   struct GNUNET_SCHEDULER_Task *rtask;
    466 
    467   /**
    468    * Reference to scheduler task to write to UART
    469    */
    470   struct GNUNET_SCHEDULER_Task *wtask;
    471 
    472   /**
    473    * Reference to the mdb cmd which will be sent next
    474    */
    475   const struct MdbCommand *cmd;
    476 
    477   /**
    478    * Reference to the mdb cmd which was sent last
    479    */
    480   const struct MdbCommand *last_cmd;
    481 
    482   /**
    483    * Current read offset in @e rxBuffer.
    484    */
    485   size_t rx_off;
    486 
    487   /**
    488    * Current write offset in @e txBuffer.
    489    */
    490   size_t tx_off;
    491 
    492   /**
    493    * Number of bytes in @e txBuffer with the serialized data of the
    494    * @e last_cmd.
    495    */
    496   size_t tx_len;
    497 
    498   /**
    499    * Time out to wait for an acknowledge received via the mdb bus
    500    */
    501   struct GNUNET_TIME_Absolute ack_timeout;
    502 
    503   /**
    504    * Backup of the config data to restore the configuration of the UART before closing it
    505    */
    506   struct termios uart_opts_backup;
    507 
    508   /**
    509    * Indicates if a vend session is running or not
    510    */
    511   bool session_running;
    512 
    513   /**
    514    * File descriptor to the UART device file
    515    */
    516   int uartfd;
    517 
    518 };
    519 
    520 
    521 /**
    522  * Handle for the Framebuffer device
    523  */
    524 struct Display
    525 {
    526   /**
    527    * File descriptor for the screen
    528    */
    529   int devicefd;
    530 
    531   /**
    532    * File descriptor to set backlight information
    533    */
    534   int backlightfd;
    535 
    536   /**
    537    * The display memory to set the pixel information
    538    */
    539   uint16_t *memory;
    540 
    541   /**
    542    * Original screen information
    543    */
    544   struct fb_var_screeninfo orig_vinfo;
    545 
    546   /**
    547    * Variable screen information (color depth ...)
    548    */
    549   struct fb_var_screeninfo var_info;
    550 
    551   /**
    552    * Fixed screen informtaion
    553    */
    554   struct fb_fix_screeninfo fix_info;
    555 };
    556 
    557 /**
    558  * Handle for the Cancel Button
    559  */
    560 struct CancelButton
    561 {
    562   /**
    563    * File descriptor to read the state of the cancel button gpio pin
    564    */
    565   int cancelbuttonfd;
    566 };
    567 
    568 
    569 /**
    570  * DLL of pending refund operations.
    571  */
    572 struct Refund
    573 {
    574   /**
    575    * DLL next pointer.
    576    */
    577   struct Refund *next;
    578 
    579   /**
    580    * DLL prev pointer.
    581    */
    582   struct Refund *prev;
    583 
    584   /**
    585    * Curl context for communication with taler backend
    586    */
    587   struct GNUNET_CURL_Context *ctx;
    588 
    589   /**
    590    * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
    591    */
    592   struct GNUNET_CURL_RescheduleContext *rc;
    593 
    594   /**
    595    * Handle to the ongoing operation.
    596    */
    597   struct TALER_MERCHANT_PostPrivateOrdersRefundHandle *orh;
    598 
    599 };
    600 
    601 
    602 /**
    603  * DLL head of refunds.
    604  */
    605 static struct Refund *refund_head;
    606 
    607 /**
    608  * DLL tail of refunds.
    609  */
    610 static struct Refund *refund_tail;
    611 
    612 /**
    613  * NFC context used by the NFC reader
    614  */
    615 static nfc_context *context;
    616 
    617 /**
    618  * Global return value
    619  */
    620 static int global_ret;
    621 
    622 /**
    623  * Flag set to remember that we are in shutdown.
    624  */
    625 static int in_shutdown;
    626 
    627 /**
    628  * Flag set to remember that MDB needs shutdown
    629  * (because we were actually able to talk to MDB).
    630  */
    631 static bool mdb_active;
    632 
    633 /**
    634  * Reference to the keyboard task
    635  */
    636 static struct GNUNET_SCHEDULER_Task *keyboard_task;
    637 
    638 /**
    639  * Reference to the cancel button task
    640  */
    641 static struct GNUNET_SCHEDULER_Task *cancelbutton_task;
    642 
    643 /**
    644  * Task to stop showing transient errors.
    645  */
    646 static struct GNUNET_SCHEDULER_Task *err_stop_task;
    647 
    648 /**
    649  * Handle to the process showing messages/advertisements
    650  * while we are inactive.
    651  */
    652 static struct GNUNET_Process *adv_child;
    653 
    654 /**
    655  * Handle to the process showing error messages
    656  * while we have one.
    657  */
    658 static struct GNUNET_Process *err_child;
    659 
    660 /**
    661  * Command to run when advertising is enabled.
    662  * Can be NULL.
    663  */
    664 static char *adv_process_command;
    665 
    666 /**
    667  * Command to run when advertising is enabled.
    668  * Can be NULL.
    669  */
    670 static char *err_process_command;
    671 
    672 /**
    673  * Taler Backend url read from configuration file
    674  */
    675 static char *backend_base_url;
    676 
    677 /**
    678  * Fulfillment message to display after successful payment, read from configuration file
    679  */
    680 static char *fulfillment_msg;
    681 
    682 /**
    683  * Should we hide a transient error when MDB is ready?
    684  */
    685 static bool clear_error_on_start;
    686 
    687 /**
    688  * Handle for the payment
    689  */
    690 static struct PaymentActivity *payment_activity;
    691 
    692 /**
    693  * Products read from configuration file
    694  */
    695 static struct Product *products;
    696 
    697 /**
    698  * Amount of products
    699  */
    700 static unsigned int products_length;
    701 
    702 /**
    703  * Data associated with the MDB session.
    704  */
    705 static struct MdbHandle mdb;
    706 
    707 /**
    708  * MDB response to the request for configuration.
    709  */
    710 static struct MdbCommand cmd_reader_config_data;
    711 
    712 /**
    713  * Ask MDB to begin session (with "infinite" money)
    714  */
    715 static struct MdbCommand cmd_begin_session;
    716 
    717 /**
    718  * Refuse vending request (payment failed)
    719  */
    720 static struct MdbCommand cmd_deny_vend;
    721 
    722 /**
    723  * Approve vending request (payment succeeded)
    724  */
    725 static struct MdbCommand cmd_approve_vend;
    726 
    727 /**
    728  * Confirm cancellation by machine.
    729  */
    730 static struct MdbCommand cmd_reader_cancelled;
    731 
    732 /**
    733  * Approve Revalue
    734  */
    735 static struct MdbCommand cmd_revalue_approved;
    736 
    737 /**
    738  * Send Revalue Limit Amount
    739  */
    740 static struct MdbCommand cmd_revalue_amount;
    741 
    742 /**
    743  * Send NACK
    744  */
    745 static struct MdbCommand cmd_reader_NACK;
    746 
    747 /**
    748  * Display Request for Sold Out
    749  */
    750 static struct MdbCommand cmd_reader_display_sold_out;
    751 
    752 /**
    753  * Display Request for Error Message
    754  */
    755 static struct MdbCommand cmd_reader_display_internal_error;
    756 
    757 /**
    758  * Display Request for Error Message
    759  */
    760 static struct MdbCommand cmd_reader_display_backend_not_reachable;
    761 
    762 /**
    763  * Terminate session.
    764  */
    765 static struct MdbCommand endSession;
    766 
    767 /**
    768  * Name of the framebuffer device (i.e. /dev/fb1).
    769  */
    770 static char *framebuffer_device_filename;
    771 
    772 /**
    773  * Name of the backlight file of @e framebuffer_device_filename (i.e. /sys/class/backlight/soc:backlight/brightness).
    774  */
    775 static char *framebuffer_backlight_filename;
    776 
    777 /**
    778  * Global option '-i' to invert backlight on/off values
    779  */
    780 static int backlight_invert;
    781 
    782 /**
    783  * Standard backlight on value
    784  */
    785 static char backlight_on = '1';
    786 
    787 /**
    788  * Standard backlight off value
    789  */
    790 static char backlight_off = '0';
    791 
    792 /**
    793  * State for the implementation of the 'cancel' button.
    794  */
    795 static struct CancelButton cancel_button;
    796 
    797 /**
    798  * Name of the UART device with the MDB (i.e. /dev/ttyAMA0).
    799  */
    800 static char *uart_device_filename;
    801 
    802 /**
    803  * Global option '-d' to disable MDB set.
    804  */
    805 static int disable_mdb;
    806 
    807 /**
    808  * Global option '-t' to disable stdin / terminal.
    809  */
    810 static int disable_tty;
    811 
    812 /**
    813  * Global option '-s' to enable sold-out detection.
    814  */
    815 static int sold_out_enabled;
    816 
    817 /**
    818  * Taler wallet application identifier
    819  */
    820 static const uint8_t taler_aid[] = { 0xF0, 0x00, 0x54, 0x41, 0x4c, 0x45, 0x52 };
    821 
    822 /**
    823  * NFC select file command to select wallet aid
    824  */
    825 static const uint8_t select_file[] = { 0x00, 0xA4, 0x04, 0x00, 0x07 };
    826 
    827 /**
    828  * NFC put command to send data to the wallet
    829  */
    830 static const uint8_t put_data[] = { 0x00, 0xDA, 0x01, 0x00, 0x7c, 0x01 };
    831 
    832 #if FUTURE_FEATURES
    833 /* tunneling */
    834 static const uint8_t get_data[] = { 0x00, 0xCA, 0x01, 0x00, 0x00, 0x00 };
    835 #endif
    836 
    837 /**
    838  * Handle for the framebuffer device
    839  */
    840 static struct Display qrDisplay;
    841 
    842 
    843 /**
    844  * Start process using the @a command command-line.
    845  *
    846  * @param command command to run
    847  * @param ... extra arguments to pass
    848  * @return process handle, NULL on failure
    849  */
    850 static struct GNUNET_Process *
    851 start_command (const char *command,
    852                ...)
    853 {
    854   char **argv = NULL;
    855   unsigned int argc = 0;
    856   char *cpy = GNUNET_strdup (command);
    857   struct GNUNET_Process *ret;
    858   va_list ap;
    859   const char *arg;
    860 
    861   for (const char *tok = strtok (cpy, " ");
    862        NULL != tok;
    863        tok = strtok (NULL, " "))
    864   {
    865     GNUNET_array_append (argv,
    866                          argc,
    867                          GNUNET_strdup (tok));
    868   }
    869   va_start (ap,
    870             command);
    871   while (NULL != (arg = va_arg (ap,
    872                                 const char *)))
    873   {
    874     GNUNET_array_append (argv,
    875                          argc,
    876                          GNUNET_strdup (arg));
    877   }
    878   va_end (ap);
    879   GNUNET_array_append (argv,
    880                        argc,
    881                        NULL);
    882   ret = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
    883   if (GNUNET_OK !=
    884       GNUNET_process_run_command_argv (ret,
    885                                        argv[0],
    886                                        (const char **) argv))
    887   {
    888     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    889                 "Failed to launch %s\n",
    890                 argv[0]);
    891     GNUNET_process_destroy (ret);
    892     ret = NULL;
    893   }
    894   for (unsigned int i = 0; i<argc; i++)
    895     GNUNET_free (argv[i]);
    896   GNUNET_array_grow (argv,
    897                      argc,
    898                      0);
    899   GNUNET_free (cpy);
    900   return ret;
    901 }
    902 
    903 
    904 /**
    905  * Stop the advertising process.
    906  */
    907 static void
    908 stop_advertising (void)
    909 {
    910   if (NULL == adv_child)
    911     return;
    912   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    913               "Stopping advertising\n");
    914   GNUNET_break (GNUNET_OK ==
    915                 GNUNET_process_kill (adv_child,
    916                                      SIGTERM));
    917   GNUNET_break (GNUNET_OK ==
    918                 GNUNET_process_wait (adv_child,
    919                                      true,
    920                                      NULL,
    921                                      NULL));
    922   GNUNET_process_destroy (adv_child);
    923   adv_child = NULL;
    924 }
    925 
    926 
    927 /**
    928  * Start the advertising process.
    929  */
    930 static void
    931 start_advertising (void)
    932 {
    933   if (NULL != err_child)
    934     return;
    935   stop_advertising (); /* just to be sure */
    936   if (NULL == adv_process_command)
    937     return;
    938   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    939               "Starting advertising\n");
    940   adv_child = start_command (adv_process_command,
    941                              NULL);
    942 }
    943 
    944 
    945 /**
    946  * Stop the process showing an error.
    947  */
    948 static void
    949 hide_error (void)
    950 {
    951   if (NULL == err_child)
    952     return;
    953   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    954               "Hiding error\n");
    955   if (NULL != err_stop_task)
    956   {
    957     GNUNET_SCHEDULER_cancel (err_stop_task);
    958     err_stop_task = NULL;
    959   }
    960   GNUNET_break (GNUNET_OK ==
    961                 GNUNET_process_kill (err_child,
    962                                      SIGTERM));
    963   GNUNET_break (GNUNET_OK ==
    964                 GNUNET_process_wait (err_child,
    965                                      true,
    966                                      NULL,
    967                                      NULL));
    968   GNUNET_process_destroy (err_child);
    969   err_child = NULL;
    970 }
    971 
    972 
    973 /**
    974  * Show an error using the respective error process
    975  * command.
    976  *
    977  * @param err_type type of the error to pass to the command
    978  */
    979 static void
    980 show_error (const char *err_type)
    981 {
    982   stop_advertising ();
    983   hide_error (); /* just to be sure */
    984   if (NULL == err_process_command)
    985   {
    986     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    987                 "Cannot show error `%s'\n",
    988                 err_type);
    989     return;
    990   }
    991   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    992               "Showing error `%s' using `%s'\n",
    993               err_type,
    994               err_process_command);
    995   err_child = start_command (err_process_command,
    996                              err_type,
    997                              NULL);
    998   GNUNET_break (NULL != err_child);
    999 }
   1000 
   1001 
   1002 /**
   1003  * Task to stop the process showing an error.
   1004  *
   1005  * @param cls NULL
   1006  */
   1007 static void
   1008 do_hide_error (void *cls)
   1009 {
   1010   err_stop_task = NULL;
   1011   hide_error ();
   1012   start_advertising ();
   1013 }
   1014 
   1015 
   1016 /**
   1017  * Briefly show a temporary error.
   1018  *
   1019  * @param err_type error to show
   1020  */
   1021 static void
   1022 temporary_error (const char *err_type)
   1023 {
   1024   show_error (err_type);
   1025   if (NULL != err_stop_task)
   1026   {
   1027     GNUNET_SCHEDULER_cancel (err_stop_task);
   1028     err_stop_task = NULL;
   1029   }
   1030   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1031               "Will hide error in %s\n",
   1032               GNUNET_TIME_relative2s (ERR_DELAY,
   1033                                       true));
   1034   err_stop_task = GNUNET_SCHEDULER_add_delayed (ERR_DELAY,
   1035                                                 &do_hide_error,
   1036                                                 NULL);
   1037 }
   1038 
   1039 
   1040 #if HAVE_QRENCODE_H
   1041 #include <qrencode.h>
   1042 
   1043 /**
   1044  * @brief Create the QR code to pay and display it on screen
   1045  *
   1046  * @param uri what text to show in the QR code
   1047  */
   1048 static void
   1049 show_qrcode (const char *uri)
   1050 {
   1051   QRinput *qri;
   1052   QRcode *qrc;
   1053   unsigned int size;
   1054   char *upper;
   1055   char *base;
   1056   char *ubase;
   1057   size_t xOff;
   1058   size_t yOff;
   1059   const char *dddash;
   1060   unsigned int nwidth;
   1061 
   1062   stop_advertising ();
   1063   hide_error ();
   1064   if (0 > qrDisplay.devicefd)
   1065     return; /* no display, no dice */
   1066   /* find the fourth '/' in the payto://pay/hostname/-uri */
   1067   dddash = strchr (uri, '/');
   1068   if (NULL != dddash)
   1069     dddash = strchr (dddash + 1, '/');
   1070   if (NULL != dddash)
   1071     dddash = strchr (dddash + 1, '/');
   1072   if (NULL != dddash)
   1073     dddash = strchr (dddash + 1, '/');
   1074   if (NULL == dddash)
   1075   {
   1076     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1077                 "taler://pay/-URI malformed: `%s'\n",
   1078                 uri);
   1079     return;
   1080   }
   1081 
   1082   qri = QRinput_new2 (0, QR_ECLEVEL_L);
   1083   if (NULL == qri)
   1084   {
   1085     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   1086                          "QRinput_new2");
   1087     return;
   1088   }
   1089   /* convert all characters until the fourth '/' to upper
   1090      case. The rest _should_ be upper case in a NICE setup,
   1091      but we can't warrant it and must not touch those. */
   1092   base = GNUNET_strndup (uri,
   1093                          dddash - uri);
   1094 
   1095   ubase = GNUNET_STRINGS_utf8_toupper (base);
   1096   GNUNET_free (base);
   1097   GNUNET_asprintf (&upper,
   1098                    "%s%s",
   1099                    ubase,
   1100                    dddash);
   1101   GNUNET_free (ubase);
   1102   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1103               "Showing QR code for `%s'\n",
   1104               upper);
   1105   /* first try encoding as uppercase-only alpha-numerical
   1106      QR code (much smaller encoding); if that fails, also
   1107      try using binary encoding (in case nick contains
   1108      special characters). */
   1109   if ( (0 !=
   1110         QRinput_append (qri,
   1111                         QR_MODE_AN,
   1112                         strlen (upper),
   1113                         (unsigned char *) upper)) &&
   1114        (0 !=
   1115         QRinput_append (qri,
   1116                         QR_MODE_8,
   1117                         strlen (upper),
   1118                         (unsigned char *) upper)))
   1119   {
   1120     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   1121                          "QRinput_append");
   1122     GNUNET_free (upper);
   1123     return;
   1124   }
   1125   GNUNET_free (upper);
   1126   qrc = QRcode_encodeInput (qri);
   1127   if (NULL == qrc)
   1128   {
   1129     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   1130                          "QRcode_encodeInput");
   1131     QRinput_free (qri);
   1132     return;
   1133   }
   1134 
   1135   /* set QR-code border */
   1136   memset (qrDisplay.memory,
   1137           0xFF,
   1138           qrDisplay.var_info.xres * qrDisplay.var_info.yres
   1139           * sizeof (uint16_t));
   1140   size = GNUNET_MIN (qrDisplay.var_info.xres,
   1141                      qrDisplay.var_info.yres);
   1142 
   1143   nwidth = qrc->width + 8; /* +8 for 4 pixel border */
   1144   xOff = 4 * size / nwidth;
   1145   yOff = 4 * size / nwidth;
   1146 
   1147   /* calculate offset to show the code centered */
   1148   if (qrDisplay.var_info.xres < qrDisplay.var_info.yres)
   1149     yOff += (qrDisplay.var_info.yres - qrDisplay.var_info.xres) / 2;
   1150   else
   1151     xOff += (qrDisplay.var_info.xres - qrDisplay.var_info.yres) / 2;
   1152   for (unsigned int x = 0; x < qrDisplay.var_info.xres - 2 * xOff; x++)
   1153     for (unsigned int y = 0; y < qrDisplay.var_info.yres - 2 * yOff; y++)
   1154     {
   1155       unsigned int xoff = x * nwidth / size;
   1156       unsigned int yoff = y * nwidth / size;
   1157       unsigned int off = xoff + yoff * qrc->width;
   1158       if ( (xoff >= (unsigned) qrc->width) ||
   1159            (yoff >= (unsigned) qrc->width) )
   1160         continue;
   1161       /* set the pixels in the display memory */
   1162       qrDisplay.memory[(y + yOff) * qrDisplay.var_info.xres + (x + xOff)] =
   1163         (0 == (qrc->data[off] & 1)) ? 0xFFFF : 0x0000;
   1164     }
   1165 
   1166   QRcode_free (qrc);
   1167   QRinput_free (qri);
   1168 
   1169   /* Turn on backlight if supported */
   1170   if (0 < qrDisplay.backlightfd)
   1171     (void) ! write (qrDisplay.backlightfd,
   1172                     &backlight_on,
   1173                     1);
   1174 }
   1175 
   1176 
   1177 #endif
   1178 
   1179 
   1180 static void
   1181 run_mdb_event_loop (void);
   1182 
   1183 
   1184 /**
   1185  * Runs asynchronous cleanup part for freeing a
   1186  * payment activity.
   1187  *
   1188  * @param[in] cls a `struct PaymentActivity` to clean up
   1189  */
   1190 static void
   1191 async_pa_cleanup_job (void *cls)
   1192 {
   1193   struct PaymentActivity *pa = cls;
   1194 
   1195   if (NULL != pa->ctx)
   1196     GNUNET_CURL_fini (pa->ctx);
   1197   if (NULL != pa->rc)
   1198     GNUNET_CURL_gnunet_rc_destroy (pa->rc);
   1199   GNUNET_free (pa);
   1200   start_advertising ();
   1201 }
   1202 
   1203 
   1204 /**
   1205  * @brief Cleanup all the data when a order has succeeded or got cancelled
   1206  *
   1207  * @param pa the payment activity to clean up
   1208  */
   1209 static void
   1210 cleanup_payment (struct PaymentActivity *pa);
   1211 
   1212 
   1213 /**
   1214  * Function called with the result of the DELETE /orders/$ID operation.
   1215  *
   1216  * @param cls closure with the `struct PaymentActivity *`
   1217  * @param dpor HTTP response details
   1218  */
   1219 static void
   1220 order_delete_cb (
   1221   void *cls,
   1222   const struct TALER_MERCHANT_DeletePrivateOrderResponse *dpor)
   1223 {
   1224   struct PaymentActivity *pa = cls;
   1225 
   1226   pa->odh = NULL;
   1227   if ( (MHD_HTTP_OK != dpor->hr.http_status) &&
   1228        (MHD_HTTP_NO_CONTENT != dpor->hr.http_status) )
   1229   {
   1230     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1231                 "Failed to delete incomplete order from backend: %d/%u\n",
   1232                 (int) dpor->hr.http_status,
   1233                 (unsigned int) dpor->hr.ec);
   1234   }
   1235   cleanup_payment (pa);
   1236 }
   1237 
   1238 
   1239 /**
   1240  * Clear the screen showing the QR code for the order.
   1241  *
   1242  * @param[in,out] pa payment activity to clear screen for
   1243  */
   1244 static void
   1245 clear_screen (struct PaymentActivity *pa)
   1246 {
   1247   if (NULL == pa->taler_pay_uri)
   1248     return;
   1249 #if HAVE_QRENCODE_H
   1250   if (NULL != qrDisplay.memory)
   1251     memset (qrDisplay.memory,
   1252             0xFF,
   1253             qrDisplay.var_info.xres * qrDisplay.var_info.yres
   1254             * sizeof (uint16_t));
   1255   if (0 < qrDisplay.backlightfd)
   1256     (void) ! write (qrDisplay.backlightfd,
   1257                     &backlight_off,
   1258                     1);
   1259 #endif
   1260   GNUNET_free (pa->taler_pay_uri);
   1261 }
   1262 
   1263 
   1264 static void
   1265 cleanup_payment (struct PaymentActivity *pa)
   1266 {
   1267   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1268               "Cleaning up payment\n");
   1269   if ( (! pa->paid) &&
   1270        (NULL != pa->order_id) )
   1271   {
   1272     char *oid;
   1273 
   1274     oid = pa->order_id;
   1275     pa->order_id = NULL;
   1276     pa->odh = TALER_MERCHANT_delete_private_order_create (
   1277       pa->ctx,
   1278       pa->base_url,
   1279       oid);
   1280     GNUNET_assert (GNUNET_OK ==
   1281                    TALER_MERCHANT_delete_private_order_set_options (
   1282                      pa->odh,
   1283                      TALER_MERCHANT_delete_private_order_set_option_force ()));
   1284     GNUNET_assert (TALER_EC_NONE ==
   1285                    TALER_MERCHANT_delete_private_order_start (
   1286                      pa->odh,
   1287                      &order_delete_cb,
   1288                      pa));
   1289     GNUNET_free (oid);
   1290     return;
   1291   }
   1292   if (NULL != pa->odh)
   1293   {
   1294     TALER_MERCHANT_delete_private_order_cancel (pa->odh);
   1295     pa->odh = NULL;
   1296   }
   1297   if (NULL != pa->pnd)
   1298   {
   1299     nfc_abort_command (pa->pnd);
   1300     nfc_close (pa->pnd);
   1301     pa->pnd = NULL;
   1302   }
   1303   if (NULL != cancelbutton_task)
   1304   {
   1305     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1306                 "Stopping watching of the cancel button\n");
   1307     GNUNET_SCHEDULER_cancel (cancelbutton_task);
   1308     cancelbutton_task = NULL;
   1309   }
   1310   if (NULL != pa->po)
   1311   {
   1312     TALER_MERCHANT_post_private_orders_cancel (pa->po);
   1313     pa->po = NULL;
   1314   }
   1315   if (NULL != pa->ogh)
   1316   {
   1317     TALER_MERCHANT_get_private_order_cancel (pa->ogh);
   1318     pa->ogh = NULL;
   1319   }
   1320   GNUNET_CURL_gnunet_scheduler_reschedule (&pa->rc);
   1321   if (NULL != pa->task)
   1322   {
   1323     GNUNET_SCHEDULER_cancel (pa->task);
   1324     pa->task = NULL;
   1325   }
   1326   if (NULL != pa->delay_task)
   1327   {
   1328     GNUNET_SCHEDULER_cancel (pa->delay_task);
   1329     pa->delay_task = NULL;
   1330   }
   1331   if (NULL != pa->delay_pay_task)
   1332   {
   1333     GNUNET_SCHEDULER_cancel (pa->delay_pay_task);
   1334     pa->delay_pay_task = NULL;
   1335   }
   1336   clear_screen (pa);
   1337   GNUNET_free (pa->order_id);
   1338   GNUNET_SCHEDULER_add_now (&async_pa_cleanup_job,
   1339                             pa);
   1340 }
   1341 
   1342 
   1343 /**
   1344  * @brief Shutdown the mdb communication tasks
   1345  */
   1346 static void
   1347 mdb_shutdown (void)
   1348 {
   1349   if (NULL != mdb.rtask)
   1350   {
   1351     GNUNET_SCHEDULER_cancel (mdb.rtask);
   1352     mdb.rtask = NULL;
   1353   }
   1354   if (NULL != mdb.wtask)
   1355   {
   1356     GNUNET_SCHEDULER_cancel (mdb.wtask);
   1357     mdb.wtask = NULL;
   1358   }
   1359   hide_error ();
   1360   stop_advertising ();
   1361   if (disable_mdb)
   1362     return;
   1363   /* restore UART */
   1364   if (0 != tcsetattr (mdb.uartfd,
   1365                       TCSAFLUSH,
   1366                       &mdb.uart_opts_backup))
   1367   {
   1368     printf ("Failed to restore uart discipline\n");
   1369     global_ret = EXIT_FAILURE;
   1370   }
   1371   if (-1 != mdb.uartfd)
   1372   {
   1373     GNUNET_break (0 == close (mdb.uartfd));
   1374     mdb.uartfd = -1;
   1375   }
   1376   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1377               "Shutdown complete (including MDB)\n");
   1378 }
   1379 
   1380 
   1381 /**
   1382  * @brief Shutdown the application.
   1383  *
   1384  * @param cls closure
   1385  */
   1386 static void
   1387 shutdown_task (void *cls)
   1388 {
   1389   struct Refund *r;
   1390 
   1391   (void) cls;
   1392   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1393               "Shutdown initiated\n");
   1394   stop_advertising ();
   1395   while (NULL != (r = refund_head))
   1396   {
   1397     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1398                 "Pending refund operation aborted due to shutdown\n");
   1399     GNUNET_CONTAINER_DLL_remove (refund_head,
   1400                                  refund_tail,
   1401                                  r);
   1402     TALER_MERCHANT_post_private_orders_refund_cancel (r->orh);
   1403     GNUNET_free (r);
   1404   }
   1405   if (NULL != context)
   1406   {
   1407     nfc_exit (context);
   1408     context = NULL;
   1409   }
   1410   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1411               "NFC down\n");
   1412   if (NULL != payment_activity)
   1413   {
   1414     cleanup_payment (payment_activity);
   1415     payment_activity = NULL;
   1416   }
   1417   if (NULL != cancelbutton_task)
   1418   {
   1419     GNUNET_SCHEDULER_cancel (cancelbutton_task);
   1420     cancelbutton_task = NULL;
   1421   }
   1422   if (NULL != keyboard_task)
   1423   {
   1424     GNUNET_SCHEDULER_cancel (keyboard_task);
   1425     keyboard_task = NULL;
   1426   }
   1427   /* last ditch saying nicely goodbye to MDB */
   1428   in_shutdown = GNUNET_YES;
   1429   mdb.cmd = &endSession;
   1430   if (-1 != mdb.uartfd)
   1431     run_mdb_event_loop ();
   1432   if ( (MAP_FAILED != qrDisplay.memory) &&
   1433        (NULL != qrDisplay.memory) )
   1434   {
   1435     /* free the display data  */
   1436     munmap (qrDisplay.memory,
   1437             qrDisplay.fix_info.smem_len);
   1438     qrDisplay.memory = NULL;
   1439     /* reset original state */
   1440     if (0 > ioctl (qrDisplay.devicefd,
   1441                    FBIOPUT_VSCREENINFO,
   1442                    &qrDisplay.orig_vinfo))
   1443     {
   1444       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1445                   "failed to reset originial display state\n");
   1446     }
   1447     /* close the device */
   1448     GNUNET_break (0 == close (qrDisplay.devicefd));
   1449     qrDisplay.devicefd = -1;
   1450     if (0 < qrDisplay.backlightfd)
   1451       GNUNET_break (0 == close (qrDisplay.backlightfd));
   1452     qrDisplay.backlightfd = -1;
   1453   }
   1454   if (-1 != cancel_button.cancelbuttonfd)
   1455   {
   1456     GNUNET_break (0 ==
   1457                   close (cancel_button.cancelbuttonfd));
   1458     cancel_button.cancelbuttonfd = -1;
   1459   }
   1460   {
   1461     int efd;
   1462 
   1463     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1464                 "Unexporting GPIO pin 23\n");
   1465     efd = open ("/sys/class/gpio/unexport",
   1466                 O_WRONLY);
   1467     if (-1 == efd)
   1468     {
   1469       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1470                   "Unable to open /gpio/unexport for cancel button\n");
   1471     }
   1472     else
   1473     {
   1474       if (2 != write (efd,
   1475                       "23",
   1476                       2))
   1477       {
   1478         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   1479                                   "write",
   1480                                   "/sys/class/gpio/unexport");
   1481 
   1482       }
   1483       GNUNET_break (0 == close (efd));
   1484     }
   1485   }
   1486   /* free the allocated productes read from config file */
   1487   if (NULL != products)
   1488   {
   1489     for (unsigned int i = 0; i < products_length; i++)
   1490     {
   1491       GNUNET_free (products[i].description);
   1492       GNUNET_free (products[i].auth_header);
   1493       GNUNET_free (products[i].instance);
   1494       GNUNET_free (products[i].preview);
   1495     }
   1496     GNUNET_array_grow (products,
   1497                        products_length,
   1498                        0);
   1499   }
   1500   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1501               "Shutdown complete (except for MDB)\n");
   1502 }
   1503 
   1504 
   1505 static void
   1506 check_payment_again (void *cls);
   1507 
   1508 
   1509 static void
   1510 connect_target (void *cls);
   1511 
   1512 
   1513 static void
   1514 wallet_select_aid (void *cls);
   1515 
   1516 
   1517 /**
   1518  * @brief Transmit the pay uri from taler to the wallet application via NFC
   1519  *
   1520  * @param cls closure
   1521  */
   1522 static void
   1523 wallet_transmit_uri (void *cls)
   1524 {
   1525   struct PaymentActivity *pa = cls;
   1526   /* response array for APDU response status word */
   1527   uint8_t response[] = { 0x00, 0x00 };
   1528   size_t slen = strlen (pa->taler_pay_uri);
   1529   uint8_t message[sizeof (put_data) + slen];
   1530 
   1531   pa->delay_task = NULL;
   1532   /* append the pay uri to the put data command */
   1533   memcpy (message, put_data, sizeof (put_data));
   1534   memcpy (&message[sizeof (put_data)], pa->taler_pay_uri, slen);
   1535   /* send the put data command via nfc */
   1536   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1537               "Sending 'PUT DATA' command for `%s' to wallet\n",
   1538               pa->taler_pay_uri);
   1539   if (0 > nfc_initiator_transceive_bytes (pa->pnd,
   1540                                           message,
   1541                                           sizeof (message),
   1542                                           response,
   1543                                           sizeof(response),
   1544                                           NFC_TIMEOUT))
   1545   {
   1546     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1547                 "Failed to send command via NFC\n");
   1548     pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1549                                          pa);
   1550     return;
   1551   }
   1552   /* check if the transmission succeeded */
   1553   if (0 != memcmp (response,
   1554                    APDU_SUCCESS,
   1555                    sizeof (response)))
   1556   {
   1557     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1558                 "'PUT DATA' command transmission failed, return code: %x%x\n",
   1559                 response[0],
   1560                 response[1]);
   1561     pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1562                                          pa);
   1563     return;
   1564   }
   1565   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1566               "'PUT DATA' command sent successfully via NFC\n");
   1567   pa->wallet_has_uri = true;
   1568   /* FIXME: or just offer Internet service here? */
   1569 
   1570   /* transmit the uri again later, there can be many external failures,
   1571      for example the taler wallet app was not opened and thus did not receive
   1572      the data */
   1573   pa->delay_task = GNUNET_SCHEDULER_add_delayed (MAX_HTTP_RETRY_FREQ,
   1574                                                  &wallet_transmit_uri,
   1575                                                  pa);
   1576 }
   1577 
   1578 
   1579 /**
   1580  * @brief Select the taler wallet app via NFC on the target selected with
   1581  * @e connect_target()
   1582  * (check if it is installed on the smartphone)
   1583  *
   1584  * @param cls closure
   1585  */
   1586 static void
   1587 wallet_select_aid (void *cls)
   1588 {
   1589   struct PaymentActivity *pa = cls;
   1590   /* response array for APDU response status word */
   1591   uint8_t response[] = { 0x00, 0x00 };
   1592   uint8_t message[sizeof(select_file) + sizeof(taler_aid)];
   1593 
   1594   pa->task = NULL;
   1595   /* append the taler wallet aid to the select file command */
   1596   memcpy (message,
   1597           select_file,
   1598           sizeof (select_file));
   1599   memcpy (&message[sizeof (select_file)],
   1600           taler_aid,
   1601           sizeof (taler_aid));
   1602 
   1603   /* send the select file command via nfc */
   1604   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1605               "Trying to find Taler wallet on NFC\n");
   1606   if (0 > nfc_initiator_transceive_bytes (pa->pnd,
   1607                                           message,
   1608                                           sizeof (message),
   1609                                           response,
   1610                                           sizeof (response),
   1611                                           NFC_TIMEOUT))
   1612   {
   1613     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1614                 "Failed to transceive with NFC app, trying to find another NFC client\n");
   1615     pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1616                                          pa);
   1617     return;
   1618   }
   1619   /* check if the transmission succeeded */
   1620   if (0 == memcmp (response,
   1621                    APDU_SUCCESS,
   1622                    sizeof (response)))
   1623   {
   1624     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1625                 "Taler wallet found over NFC\n");
   1626     pa->delay_task = GNUNET_SCHEDULER_add_now (&wallet_transmit_uri,
   1627                                                pa);
   1628     return;
   1629   }
   1630   /* if the transmission was not successful chack if the app is available at all */
   1631   if (0 == memcmp (response,
   1632                    APDU_NOT_FOUND,
   1633                    sizeof (response)))
   1634   {
   1635     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1636                 "Taler wallet NOT found on this device\n");
   1637     pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1638                                          pa);
   1639     return;
   1640   }
   1641   /* If the upper cases did not match, there was an unknown APDU status returned */
   1642   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1643               "AID selection failure, return code: %x%x, trying to find another NFC client\n",
   1644               response[0],
   1645               response[1]);
   1646   /* start the selection again */
   1647   pa->task = GNUNET_SCHEDULER_add_delayed (NFC_NOT_FOUND_RETRY_FREQ,
   1648                                            &connect_target,
   1649                                            pa);
   1650 }
   1651 
   1652 
   1653 /**
   1654  * @brief Connect the NFC reader with a compatible NFC target
   1655  *
   1656  * @param cls closure
   1657  */
   1658 static void
   1659 connect_target (void *cls)
   1660 {
   1661   struct PaymentActivity *pa = cls;
   1662   /* nfc modulation used */
   1663   const nfc_modulation nmMifare = {
   1664     .nmt = NMT_ISO14443A,
   1665     .nbr = NBR_212,
   1666   };
   1667 
   1668   pa->task = NULL;
   1669   /* set the uid len to zero (maybe it is still set from earlier selections) */
   1670   pa->nt.nti.nai.szUidLen = 0;
   1671   /* poll for a fitting nfc target (we use the shortest time possible to not block the scheduler) */
   1672   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1673               "Trying to find NFC client\n");
   1674   if (0 > nfc_initiator_poll_target (pa->pnd,
   1675                                      &nmMifare,
   1676                                      1,
   1677                                      0x01,
   1678                                      0x01,
   1679                                      &pa->nt))
   1680   {
   1681     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1682                 "Failed to connect to NFC target\n");
   1683   }
   1684   /* if the uid length are out of bound abort */
   1685   else if ( (pa->nt.nti.nai.szUidLen > UID_LEN_UPPER) ||
   1686             (pa->nt.nti.nai.szUidLen < UID_LEN_LOWER) )
   1687   {
   1688     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1689                 "Failed to connect, wrong NFC modulation\n");
   1690   }
   1691   else
   1692   {
   1693     /* the target was successfully selected,
   1694        now we have to check if the taler wallet is installed on it */
   1695     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1696                 "Found NFC client\n");
   1697     pa->task = GNUNET_SCHEDULER_add_now (&wallet_select_aid,
   1698                                          pa);
   1699     return;
   1700   }
   1701   /* if no target was found try again */
   1702   pa->task = GNUNET_SCHEDULER_add_delayed (NFC_NOT_FOUND_RETRY_FREQ,
   1703                                            &connect_target,
   1704                                            pa);
   1705 }
   1706 
   1707 
   1708 /**
   1709  * @brief Open the NFC reader.
   1710  *
   1711  * @param cls closure
   1712  */
   1713 static void
   1714 open_nfc_reader (void *cls)
   1715 {
   1716   struct PaymentActivity *pa = cls;
   1717 
   1718   pa->task = NULL;
   1719   /* open the nfc reader via libnfc's open */
   1720   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1721               "Trying to open NFC device\n");
   1722   pa->pnd = nfc_open (context, NULL);
   1723   if (NULL == pa->pnd)
   1724   {
   1725     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1726                 "Payment inititation: Unable to open NFC device\n");
   1727     pa->task = GNUNET_SCHEDULER_add_delayed (NFC_FAILURE_RETRY_FREQ,
   1728                                              &open_nfc_reader,
   1729                                              pa);
   1730     return;
   1731   }
   1732   /* initialize the reader as initiator */
   1733   if (0 > nfc_initiator_init (pa->pnd))
   1734   {
   1735     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1736                 "Failed to initialize NFC device: %s\n",
   1737                 nfc_strerror (pa->pnd));
   1738     cleanup_payment (pa);
   1739     GNUNET_assert (payment_activity == pa);
   1740     payment_activity = NULL;
   1741     return;
   1742   }
   1743   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1744               "NFC in operation %s / %s\n",
   1745               nfc_device_get_name (pa->pnd),
   1746               nfc_device_get_connstring (pa->pnd));
   1747   /* the nfc reader was opened successfully, now try to find a mobile device as a target */
   1748   pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1749                                        pa);
   1750 }
   1751 
   1752 
   1753 static void
   1754 start_read_keyboard (void);
   1755 
   1756 
   1757 /**
   1758  * @brief Callback to process a GET /private/orders/$ORDER_ID request
   1759  *
   1760  * @param cls closure
   1761  * @param osr order status response details (on success)
   1762  */
   1763 static void
   1764 check_payment_cb (
   1765   void *cls,
   1766   const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
   1767 {
   1768   struct PaymentActivity *pa = cls;
   1769   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
   1770 
   1771   GNUNET_assert (payment_activity == pa);
   1772   pa->ogh = NULL;
   1773   if ( (MHD_HTTP_OK != hr->http_status) &&
   1774        (MHD_HTTP_GATEWAY_TIMEOUT != hr->http_status) &&
   1775        (MHD_HTTP_REQUEST_TIMEOUT != hr->http_status) )
   1776   {
   1777     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1778                 "Backend request to GET /orders/$ID failed: %u/%d\n",
   1779                 hr->http_status,
   1780                 (int) hr->ec);
   1781     mdb.cmd = &cmd_reader_display_backend_not_reachable;
   1782     temporary_error ("backend-unexpected-failure");
   1783     run_mdb_event_loop ();
   1784     cleanup_payment (pa);
   1785     GNUNET_assert (payment_activity == pa);
   1786     payment_activity = NULL;
   1787     return;
   1788   }
   1789 
   1790   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1791               "Backend request to GET /orders/$ID returned: %u\n",
   1792               hr->http_status);
   1793   if ( (MHD_HTTP_OK == hr->http_status) &&
   1794        (TALER_MERCHANT_OSC_PAID == osr->details.ok.status) )
   1795   {
   1796     clear_screen (pa);
   1797     clear_error_on_start = true;
   1798     temporary_error ("dispensing");
   1799     mdb.cmd = &cmd_approve_vend;
   1800     payment_activity->paid = true;
   1801     run_mdb_event_loop ();
   1802     if ((disable_mdb) && (! disable_tty))
   1803     {
   1804       GNUNET_SCHEDULER_cancel (keyboard_task);
   1805       keyboard_task = NULL;
   1806       start_read_keyboard ();
   1807     }
   1808     return;
   1809   }
   1810   /* Start to check for payment. Note that we do this even before
   1811      we talked successfully to the wallet via NFC because we MAY show the
   1812      QR code in the future and in that case the payment may happen
   1813      anytime even before the NFC communication succeeds. */
   1814   if ( (NULL == pa->ogh) &&
   1815        (NULL == pa->delay_pay_task) )
   1816   {
   1817     pa->delay_pay_task = GNUNET_SCHEDULER_add_delayed (MAX_HTTP_RETRY_FREQ,
   1818                                                        &check_payment_again,
   1819                                                        pa);
   1820   }
   1821   if ( (NULL == pa->taler_pay_uri) &&
   1822        (MHD_HTTP_OK == hr->http_status) &&
   1823        (TALER_MERCHANT_OSC_UNPAID == osr->details.ok.status) )
   1824   {
   1825     char *uri;
   1826 
   1827     uri = GNUNET_strdup (osr->details.ok.details.unpaid.taler_pay_uri);
   1828     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1829                 "Trying to talk to wallet to give it pay URI `%s'\n",
   1830                 uri);
   1831     GNUNET_assert (NULL == pa->pnd);
   1832     pa->taler_pay_uri = uri;
   1833 #if HAVE_QRENCODE_H
   1834     show_qrcode (uri);
   1835 #endif
   1836     pa->task = GNUNET_SCHEDULER_add_now (&open_nfc_reader,
   1837                                          pa);
   1838   }
   1839 }
   1840 
   1841 
   1842 /**
   1843  * @brief Check the payment status again
   1844  *
   1845  * @param cls closure
   1846  */
   1847 static void
   1848 check_payment_again (void *cls)
   1849 {
   1850   struct PaymentActivity *pa = cls;
   1851 
   1852   pa->delay_pay_task = NULL;
   1853   GNUNET_assert (NULL == pa->ogh);
   1854   pa->ogh = TALER_MERCHANT_get_private_order_create (
   1855     pa->ctx,
   1856     pa->base_url,
   1857     pa->order_id);
   1858   GNUNET_assert (GNUNET_OK ==
   1859                  TALER_MERCHANT_get_private_order_set_options (
   1860                    pa->ogh,
   1861                    TALER_MERCHANT_get_private_order_option_timeout (
   1862                      BACKEND_POLL_TIMEOUT)));
   1863   GNUNET_assert (TALER_EC_NONE ==
   1864                  TALER_MERCHANT_get_private_order_start (
   1865                    pa->ogh,
   1866                    &check_payment_cb,
   1867                    pa));
   1868 }
   1869 
   1870 
   1871 /**
   1872  * @brief Callback for a POST /private/orders request
   1873  *
   1874  * @param cls closure
   1875  * @param por response for this request
   1876  */
   1877 static void
   1878 proposal_cb (
   1879   void *cls,
   1880   const struct TALER_MERCHANT_PostPrivateOrdersResponse *por)
   1881 {
   1882   struct PaymentActivity *pa = cls;
   1883 
   1884   pa->po = NULL;
   1885   if (MHD_HTTP_OK != por->hr.http_status)
   1886   {
   1887     /* FIXME: In the future, we may want to support MHD_HTTP_GONE
   1888        explicitly and show 'product out of stock' here! */
   1889     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1890                 "Failed to setup order with backend: %u/%d\n",
   1891                 por->hr.http_status,
   1892                 (int) por->hr.ec);
   1893     json_dumpf (por->hr.reply,
   1894                 stderr,
   1895                 0);
   1896     temporary_error ("backend-temporary-failure");
   1897     mdb.cmd = &cmd_reader_display_backend_not_reachable;
   1898     run_mdb_event_loop ();
   1899     cleanup_payment (pa);
   1900     GNUNET_assert (payment_activity == pa);
   1901     payment_activity = NULL;
   1902     return;
   1903   }
   1904   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1905               "Backend successfully created order `%s'\n",
   1906               por->details.ok.order_id);
   1907   pa->order_id = GNUNET_strdup (por->details.ok.order_id);
   1908   pa->ogh = TALER_MERCHANT_get_private_order_create (pa->ctx,
   1909                                                      pa->base_url,
   1910                                                      pa->order_id);
   1911   GNUNET_assert (NULL != pa->ogh);
   1912   GNUNET_assert (TALER_EC_NONE ==
   1913                  TALER_MERCHANT_get_private_order_start (
   1914                    pa->ogh,
   1915                    &check_payment_cb,
   1916                    pa));
   1917 }
   1918 
   1919 
   1920 static void
   1921 start_read_cancel_button (void);
   1922 
   1923 
   1924 /**
   1925  * @brief Launch a new order
   1926  *
   1927  * @param product information for product to sell
   1928  * @return payment activity for the order, NULL on failure
   1929  */
   1930 static struct PaymentActivity *
   1931 launch_payment (struct Product *product)
   1932 {
   1933   struct PaymentActivity *pa;
   1934   json_t *orderReq;
   1935   char *msg;
   1936   const char *pos;
   1937 
   1938   pos = strstr (fulfillment_msg,
   1939                 "${PRODUCT_DESCRIPTION}");
   1940   if (NULL != pos)
   1941   {
   1942     /* replace ${PRODUCT_DESCRIPTION} with the real one */
   1943     GNUNET_asprintf (&msg,
   1944                      "%.*s%s%s",
   1945                      /* first output URL until ${PRODUCT_DESCRIPTION} */
   1946                      (int) (pos - fulfillment_msg),
   1947                      fulfillment_msg,
   1948                      /* replace ${PRODUCT_DESCRIPTION} with the right description */
   1949                      product->description,
   1950                      /* append rest of original URL */
   1951                      pos + strlen ("${PRODUCT_DESCRIPTION}"));
   1952   }
   1953   else
   1954   {
   1955     msg = GNUNET_strdup (fulfillment_msg);
   1956   }
   1957 
   1958   /* create the json object for the order request */
   1959   if (NULL != product->preview)
   1960   {
   1961     json_t *lproducts;
   1962 
   1963     lproducts = json_array ();
   1964     GNUNET_assert (NULL != lproducts);
   1965     GNUNET_assert (
   1966       0 ==
   1967       json_array_append_new (lproducts,
   1968                              GNUNET_JSON_PACK (
   1969                                GNUNET_JSON_pack_string ("description",
   1970                                                         product->description),
   1971                                GNUNET_JSON_pack_string ("image",
   1972                                                         product->preview),
   1973                                TALER_JSON_pack_amount ("price",
   1974                                                        &product->price),
   1975                                GNUNET_JSON_pack_uint64 ("quantity",
   1976                                                         1))));
   1977     orderReq = GNUNET_JSON_PACK (
   1978       GNUNET_JSON_pack_string ("summary",
   1979                                product->description),
   1980 #if BUG
   1981       GNUNET_JSON_pack_timestamp ("pay_deadline",
   1982                                   GNUNET_TIME_relative_to_timestamp (
   1983                                     PAY_TIMEOUT)),
   1984 #endif
   1985       GNUNET_JSON_pack_array_steal (
   1986         "products",
   1987         lproducts),
   1988       TALER_JSON_pack_amount ("amount",
   1989                               &product->price),
   1990       GNUNET_JSON_pack_string ("fulfillment_message",
   1991                                msg),
   1992       GNUNET_JSON_pack_time_rel ("auto_refund",
   1993                                  MAX_REFUND_DELAY));
   1994   }
   1995   else
   1996   {
   1997     orderReq = GNUNET_JSON_PACK (
   1998       GNUNET_JSON_pack_string ("summary",
   1999                                product->description),
   2000 #if BUG
   2001       GNUNET_JSON_pack_timestamp ("pay_deadline",
   2002                                   GNUNET_TIME_relative_to_timestamp (
   2003                                     PAY_TIMEOUT)),
   2004 #endif
   2005       TALER_JSON_pack_amount ("amount",
   2006                               &product->price),
   2007       GNUNET_JSON_pack_string ("fulfillment_message",
   2008                                msg),
   2009       GNUNET_JSON_pack_time_rel ("auto_refund",
   2010                                  MAX_REFUND_DELAY));
   2011   }
   2012   GNUNET_free (msg);
   2013   if (NULL == orderReq)
   2014   {
   2015     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2016                 "json_pack failed (out of memory?)\n");
   2017     return NULL;
   2018   }
   2019   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2020               "Creating order for `%s' at backend `%s'\n",
   2021               product->description,
   2022               (NULL == product->instance)
   2023               ? backend_base_url
   2024               : product->instance);
   2025   pa = GNUNET_new (struct PaymentActivity);
   2026   pa->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   2027                               &pa->rc);
   2028   pa->rc = GNUNET_CURL_gnunet_rc_create (pa->ctx);
   2029   GNUNET_assert (GNUNET_OK ==
   2030                  GNUNET_CURL_append_header (pa->ctx,
   2031                                             product->auth_header));
   2032   pa->product = product;
   2033   pa->amount = product->price;
   2034   /* put the order on the merchant's backend */
   2035   pa->base_url = (NULL == product->instance)
   2036                  ? backend_base_url
   2037                  : product->instance;
   2038   GNUNET_assert (NULL == pa->po);
   2039   pa->po = TALER_MERCHANT_post_private_orders_create (pa->ctx,
   2040                                                       pa->base_url,
   2041                                                       orderReq);
   2042   json_decref (orderReq);
   2043   if (NULL == pa->po)
   2044   {
   2045     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2046                 "TALER_MERCHANT_order_put failed (out of memory?)\n");
   2047     temporary_error ("internal-failure");
   2048     cleanup_payment (pa);
   2049     return NULL;
   2050   }
   2051   GNUNET_assert (GNUNET_OK ==
   2052                  TALER_MERCHANT_post_private_orders_set_options (
   2053                    pa->po,
   2054                    TALER_MERCHANT_post_private_orders_option_refund_delay (
   2055                      MAX_REFUND_DELAY)));
   2056   GNUNET_assert (TALER_EC_NONE ==
   2057                  TALER_MERCHANT_post_private_orders_start (
   2058                    pa->po,
   2059                    &proposal_cb,
   2060                    pa));
   2061   /* Start to read the button on the VM to cancel this payment */
   2062   if (-1 != cancel_button.cancelbuttonfd)
   2063   {
   2064     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2065                 "Payment started, watching for cancel button\n");
   2066     start_read_cancel_button ();
   2067   }
   2068   return pa;
   2069 }
   2070 
   2071 
   2072 /**
   2073  * @brief Vending successful, conclude payment activity.
   2074  */
   2075 static void
   2076 vend_success (void)
   2077 {
   2078   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2079               "MDB vend success received\n");
   2080   GNUNET_break (NULL != payment_activity);
   2081   if (NULL != payment_activity)
   2082   {
   2083     cleanup_payment (payment_activity);
   2084     payment_activity = NULL;
   2085   }
   2086   if (clear_error_on_start)
   2087   {
   2088     hide_error ();
   2089     start_advertising ();
   2090   }
   2091 }
   2092 
   2093 
   2094 /**
   2095  * Runs asynchronous cleanup part for freeing a
   2096  * refund activity.
   2097  *
   2098  * @param[in] cls a `struct Refund` to clean up
   2099  */
   2100 static void
   2101 async_refund_cleanup_job (void *cls)
   2102 {
   2103   struct Refund *r = cls;
   2104 
   2105   if (NULL != r->ctx)
   2106     GNUNET_CURL_fini (r->ctx);
   2107   if (NULL != r->rc)
   2108     GNUNET_CURL_gnunet_rc_destroy (r->rc);
   2109   GNUNET_free (r);
   2110 }
   2111 
   2112 
   2113 /**
   2114  * @brief Callback to process a POST /refund request
   2115  *
   2116  * @param cls closure
   2117  * @param rr response details
   2118  */
   2119 static void
   2120 refund_complete_cb (
   2121   void *cls,
   2122   const struct TALER_MERCHANT_PostPrivateOrdersRefundResponse *rr)
   2123 {
   2124   struct Refund *r = cls;
   2125 
   2126   r->orh = NULL;
   2127   if (MHD_HTTP_OK != rr->hr.http_status)
   2128   {
   2129     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2130                 "Failed to grant consumer refund: %u/%d\n",
   2131                 rr->hr.http_status,
   2132                 (int) rr->hr.ec);
   2133   }
   2134   GNUNET_CONTAINER_DLL_remove (refund_head,
   2135                                refund_tail,
   2136                                r);
   2137   GNUNET_SCHEDULER_add_now (&async_refund_cleanup_job,
   2138                             r);
   2139 }
   2140 
   2141 
   2142 /**
   2143  * @brief Vending failed, provide refund.
   2144  */
   2145 static void
   2146 vend_failure (void)
   2147 {
   2148   struct Product *p;
   2149   struct Refund *r;
   2150 
   2151   if (NULL == payment_activity)
   2152   {
   2153     GNUNET_break (0);
   2154     return;
   2155   }
   2156   p = payment_activity->product;
   2157   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2158               "Received MDB vend failure for `%s', refunding customer\n",
   2159               p->description);
   2160   p->sold_out = true;
   2161   mdb.cmd = &endSession;
   2162   mdb.session_running = false;
   2163   r = GNUNET_new (struct Refund);
   2164   r->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   2165                              &r->rc);
   2166   r->rc = GNUNET_CURL_gnunet_rc_create (r->ctx);
   2167   GNUNET_assert (GNUNET_OK ==
   2168                  GNUNET_CURL_append_header (r->ctx,
   2169                                             p->auth_header));
   2170   r->orh = TALER_MERCHANT_post_private_orders_refund_create (
   2171     r->ctx,
   2172     (NULL == p->instance)
   2173     ? backend_base_url
   2174     : p->instance,
   2175     payment_activity->order_id,
   2176     &payment_activity->amount,
   2177     "failed to dispense product");
   2178   if (NULL == r->orh)
   2179   {
   2180     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2181                 "Failed to launch refund interaction with the merchant backend!\n");
   2182     GNUNET_free (r);
   2183     cleanup_payment (payment_activity);
   2184     payment_activity = NULL;
   2185     return;
   2186   }
   2187   GNUNET_assert (TALER_EC_NONE ==
   2188                  TALER_MERCHANT_post_private_orders_refund_start (
   2189                    r->orh,
   2190                    &refund_complete_cb,
   2191                    r));
   2192   GNUNET_CONTAINER_DLL_insert (refund_head,
   2193                                refund_tail,
   2194                                r);
   2195   if (NULL != payment_activity)
   2196   {
   2197     cleanup_payment (payment_activity);
   2198     payment_activity = NULL;
   2199   }
   2200 }
   2201 
   2202 
   2203 /**
   2204  * @brief Read the character from stdin and activate the selected task
   2205  *
   2206  * @param cls closure
   2207  */
   2208 static void
   2209 read_keyboard_command (void *cls)
   2210 {
   2211   int input;
   2212 
   2213   (void) cls;
   2214   keyboard_task = NULL;
   2215   input = getchar ();
   2216   if ( (EOF == input) ||
   2217        ('x' == (char) input) )
   2218   {
   2219     GNUNET_SCHEDULER_shutdown ();
   2220     return;
   2221   }
   2222   if (NULL != payment_activity)
   2223   {
   2224     switch ((char) input)
   2225     {
   2226     case 'c':
   2227       if (GNUNET_NO == payment_activity->paid)
   2228       {
   2229         mdb.cmd = &cmd_deny_vend;
   2230       }
   2231       else
   2232       {
   2233         mdb.cmd = &endSession;
   2234         mdb.session_running = false;
   2235       }
   2236       run_mdb_event_loop ();
   2237       cleanup_payment (payment_activity);
   2238       payment_activity = NULL;
   2239       break;
   2240     case 'a':
   2241       payment_activity->paid = true;
   2242       mdb.cmd = &cmd_approve_vend;
   2243       run_mdb_event_loop ();
   2244       break;
   2245     case 'n':
   2246       if (disable_mdb)
   2247       {
   2248         vend_failure ();
   2249       }
   2250       else
   2251       {
   2252         fprintf (stderr,
   2253                  "Cannot fail to vend at this time, waiting for payment\n");
   2254       }
   2255       break;
   2256     case 'y':
   2257       if (disable_mdb)
   2258       {
   2259         vend_success ();
   2260       }
   2261       else
   2262       {
   2263         fprintf (stderr,
   2264                  "Cannot succeed to vend at this time, waiting for payment\n");
   2265       }
   2266       break;
   2267     default:
   2268       fprintf (stderr,
   2269                "Unknown command `%c'\n",
   2270                input);
   2271       break;
   2272     }
   2273     start_read_keyboard ();
   2274     return;
   2275   }
   2276   if (NULL != payment_activity)
   2277   {
   2278     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2279                 "Purchase activity already pending\n");
   2280     start_read_keyboard ();
   2281     return;
   2282   }
   2283   for (unsigned int i = 0; i < products_length; i++)
   2284     if (((char) input) == products[i].key)
   2285     {
   2286       if ( (sold_out_enabled) &&
   2287            (products[i].sold_out) )
   2288       {
   2289         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2290                     "Product %s sold out, denying vend\n",
   2291                     products[i].description);
   2292         mdb.cmd = &cmd_reader_display_sold_out;
   2293         run_mdb_event_loop ();
   2294         start_read_keyboard ();
   2295         return;
   2296       }
   2297       payment_activity = launch_payment (&products[i]);
   2298       start_read_keyboard ();
   2299       return;
   2300     }
   2301   fprintf (stderr,
   2302            "Unknown command '%c'\n",
   2303            (char) input);
   2304   start_read_keyboard ();
   2305 }
   2306 
   2307 
   2308 /**
   2309  * @brief Read the state of the cancel button GPIO pin
   2310  *
   2311  * @param cls closure
   2312  */
   2313 static void
   2314 cancel_button_pressed (void *cls)
   2315 {
   2316   char value;
   2317 
   2318   (void) cls;
   2319   cancelbutton_task = NULL;
   2320   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2321               "Cancel button event detected\n");
   2322   if (1 !=
   2323       read (cancel_button.cancelbuttonfd,
   2324             &value,
   2325             1))
   2326   {
   2327     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
   2328                          "read");
   2329     start_read_cancel_button ();
   2330     return;
   2331   }
   2332 
   2333   GNUNET_break (0 == lseek (cancel_button.cancelbuttonfd,
   2334                             0,
   2335                             SEEK_SET));
   2336   /* This point should only be reached  when a order is pending, because
   2337    * the scheduler read file gets added in the function "launch_payment".
   2338    * But anyway safe check, else do nothing */
   2339   if (NULL != payment_activity)
   2340   {
   2341     if ('1' == value)
   2342     {
   2343       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2344                   "Cancel button pressed, canceling current order\n");
   2345       if (GNUNET_NO == payment_activity->paid)
   2346       {
   2347         /* The current payment was not paid already, deny it */
   2348         mdb.cmd = &cmd_deny_vend;
   2349       }
   2350       else
   2351       {
   2352         /* The order was paid and if we know this, then it is also yielded,
   2353          * just end the current session */
   2354         mdb.cmd = &endSession;
   2355         mdb.session_running = false;
   2356       }
   2357       run_mdb_event_loop ();
   2358       cleanup_payment (payment_activity);
   2359       payment_activity = NULL;
   2360     }
   2361     else
   2362     {
   2363       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2364                   "Cancel button spurious event (%d), looking for more\n",
   2365                   (int) value);
   2366       start_read_cancel_button ();
   2367     }
   2368   }
   2369 }
   2370 
   2371 
   2372 /**
   2373  * @brief Wait for a keyboard input
   2374  */
   2375 static void
   2376 start_read_keyboard (void)
   2377 {
   2378   static struct GNUNET_DISK_FileHandle fh = { STDIN_FILENO };
   2379 
   2380   GNUNET_assert (NULL == keyboard_task);
   2381   if (NULL == payment_activity)
   2382   {
   2383     for (unsigned int i = 0; i < products_length; i++)
   2384       printf ("'%c' to buy %s\n",
   2385               products[i].key,
   2386               products[i].description);
   2387   }
   2388   else
   2389   {
   2390     if (GNUNET_NO == payment_activity->paid)
   2391     {
   2392       printf ("'a' to fake payment for the last purchase\n"
   2393               "'c' to cancel last purchase\n");
   2394     }
   2395     else
   2396     {
   2397       if (disable_mdb)
   2398         printf ("'y' to simulate product successfully dispensed\n"
   2399                 "'n' to simulate product failed to be dispensed\n");
   2400     }
   2401   }
   2402   printf ("'x' to quit\n");
   2403   printf ("Waiting for keyboard input\n");
   2404   keyboard_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
   2405                                                   &fh,
   2406                                                   &read_keyboard_command,
   2407                                                   NULL);
   2408 }
   2409 
   2410 
   2411 /**
   2412  * @brief Wait for cancel button during payment activity
   2413  */
   2414 static void
   2415 start_read_cancel_button (void)
   2416 {
   2417   struct GNUNET_DISK_FileHandle fh = {
   2418     cancel_button.cancelbuttonfd
   2419   };
   2420 
   2421   if (NULL != cancelbutton_task)
   2422   {
   2423     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2424                 "Cancel task still active (!?), not starting another one\n");
   2425     return;
   2426   }
   2427   cancelbutton_task = GNUNET_SCHEDULER_add_read_file (
   2428     GNUNET_TIME_UNIT_FOREVER_REL,
   2429     &fh,
   2430     &cancel_button_pressed,
   2431     NULL);
   2432 }
   2433 
   2434 
   2435 /**
   2436  * @brief Send data to the vmc via the uart bus
   2437  *
   2438  * @param cls closure
   2439  */
   2440 static void
   2441 write_mdb_command (void *cls)
   2442 {
   2443   int did_write = 0;
   2444 
   2445   (void) cls;
   2446   mdb.wtask = NULL;
   2447 
   2448   if (disable_mdb)
   2449   {
   2450     /* check if there is a cmd to send and overwrite last command */
   2451     if (NULL == mdb.cmd)
   2452       return;
   2453     mdb.last_cmd = mdb.cmd;
   2454     mdb.cmd = NULL;
   2455     run_mdb_event_loop ();
   2456     return;
   2457   }
   2458   /* if command was sent partially, send the rest */
   2459   if (mdb.tx_off < mdb.tx_len)
   2460   {
   2461     ssize_t ret = write (mdb.uartfd,
   2462                          &mdb.txBuffer[mdb.tx_off],
   2463                          mdb.tx_len - mdb.tx_off);
   2464     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2465                 "Wrote %d bytes to MDB\n",
   2466                 (int) ret);
   2467     did_write = 1;
   2468     if (-1 == ret)
   2469     {
   2470       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   2471                                 "write",
   2472                                 uart_device_filename);
   2473       if (in_shutdown)
   2474         mdb_shutdown ();
   2475       else
   2476         GNUNET_SCHEDULER_shutdown ();
   2477       return;
   2478     }
   2479     mdb.tx_off += ret;
   2480     /* if command was sent sucessfully start the timer for ACK timeout */
   2481     if ( (ret > 0) &&
   2482          (mdb.tx_off == mdb.tx_len) )
   2483       mdb.ack_timeout = GNUNET_TIME_relative_to_absolute (MAX_ACK_LATENCY);
   2484   }
   2485   /* ongoing write incomplete, continue later */
   2486   if (mdb.tx_off < mdb.tx_len)
   2487   {
   2488     run_mdb_event_loop ();
   2489     return;
   2490   }
   2491   if (NULL != mdb.last_cmd)
   2492   {
   2493     struct GNUNET_TIME_Relative del;
   2494 
   2495     /* Still waiting for ACK! -> delay write task  */
   2496     del = GNUNET_TIME_absolute_get_remaining (mdb.ack_timeout);
   2497     if (0 != del.rel_value_us)
   2498     {
   2499       if (did_write)
   2500         run_mdb_event_loop ();
   2501       else
   2502         mdb.wtask = GNUNET_SCHEDULER_add_delayed (del,
   2503                                                   &write_mdb_command,
   2504                                                   NULL);
   2505       return;
   2506     }
   2507     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2508                 "MDB failed to acknowledge command `%s' within timeout\n",
   2509                 mdb.last_cmd->name);
   2510     mdb.last_cmd = NULL;
   2511     if (in_shutdown)
   2512     {
   2513       mdb_shutdown ();
   2514       return;
   2515     }
   2516   }
   2517   if (NULL == mdb.cmd)
   2518     return;
   2519   /* if there is a command to send calculate length and checksum */
   2520   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2521               "Sending command `%s'\n",
   2522               mdb.cmd->name);
   2523   mdb.tx_off = 0;
   2524   mdb.tx_len = mdb.cmd->cmd.bin_size + mdb.cmd->data.bin_size + 1;
   2525   GNUNET_assert (mdb.tx_len <= sizeof (mdb.txBuffer));
   2526   {
   2527     /* calculate checksum: sum up command and data, take just the LSB of the result */
   2528     uint32_t chkSum = 0;
   2529 
   2530     for (size_t idx = 0; idx < mdb.cmd->cmd.bin_size; idx++)
   2531       chkSum += mdb.txBuffer[idx] = mdb.cmd->cmd.bin[idx];
   2532     for (size_t idx = 0; idx < mdb.cmd->data.bin_size; idx++)
   2533       chkSum += mdb.txBuffer[idx + mdb.cmd->cmd.bin_size] =
   2534         mdb.cmd->data.bin[idx];
   2535     mdb.txBuffer[mdb.cmd->cmd.bin_size + mdb.cmd->data.bin_size] =
   2536       (uint8_t) (chkSum & 0xFF);
   2537   }
   2538   mdb.last_cmd = mdb.cmd;
   2539   mdb.cmd = NULL;
   2540   run_mdb_event_loop ();
   2541 }
   2542 
   2543 
   2544 /**
   2545  * @brief MDB acknowledged the last command, proceed.
   2546  */
   2547 static void
   2548 handle_ack ()
   2549 {
   2550   if (&cmd_begin_session == mdb.last_cmd)
   2551     mdb.session_running = true;
   2552   if (&cmd_deny_vend == mdb.last_cmd)
   2553   {
   2554     mdb.session_running = false;
   2555     mdb.cmd = &endSession;
   2556   }
   2557   if (&cmd_reader_display_sold_out == mdb.last_cmd)
   2558     mdb.cmd = &cmd_deny_vend;
   2559   if (&cmd_reader_display_internal_error == mdb.last_cmd)
   2560     mdb.cmd = &cmd_deny_vend;
   2561   if (&cmd_reader_display_backend_not_reachable == mdb.last_cmd)
   2562     mdb.cmd = &cmd_deny_vend;
   2563   mdb.last_cmd = NULL;
   2564   /* Cause the write-task to be re-scheduled now */
   2565   if (NULL != mdb.wtask)
   2566   {
   2567     GNUNET_SCHEDULER_cancel (mdb.wtask);
   2568     mdb.wtask = NULL;
   2569   }
   2570 }
   2571 
   2572 
   2573 /**
   2574  * @brief Parse received command from the VMC
   2575  *
   2576  * @param hex received command from VMC
   2577  * @param hex_len number of characters in @a hex
   2578  */
   2579 static void
   2580 handle_command (const char *hex,
   2581                 size_t hex_len)
   2582 {
   2583   unsigned int cmd;
   2584   unsigned int tmp = 0;
   2585   uint32_t chkSum;
   2586 
   2587   /* if the received command is 0 or not a multiple of 2 we cannot parse it */
   2588   if (0 == hex_len)
   2589     return;
   2590   if (0 != (hex_len % 2))
   2591   {
   2592     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2593                 "Received unexpected input `%.*s'\n",
   2594                 (int) hex_len,
   2595                 hex);
   2596     GNUNET_break_op (0);
   2597     return;
   2598   }
   2599   /* convert the received 2 bytes from ASCII to hex */
   2600   if (1 != sscanf (hex,
   2601                    "%2X",
   2602                    &cmd))
   2603   {
   2604     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2605                 "Received non-HEX input `%.*s'\n",
   2606                 (int) hex_len,
   2607                 hex);
   2608     GNUNET_break_op (0);
   2609     return;
   2610   }
   2611 
   2612   /* parse the first byte (cmd) and the second byte (subcmd) */
   2613   switch (cmd)
   2614   {
   2615   case VMC_VEND:
   2616     {
   2617       unsigned int subcmd;
   2618 
   2619       if (4 > hex_len)
   2620       {
   2621         GNUNET_break_op (0);
   2622         return;
   2623       }
   2624       if (1 != sscanf (&hex[2],
   2625                        "%2X",
   2626                        &subcmd))
   2627       {
   2628         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2629                     "Received non-HEX input `%.*s'\n",
   2630                     (int) hex_len - 2,
   2631                     &hex[2]);
   2632         GNUNET_break_op (0);
   2633         return;
   2634       }
   2635       switch (subcmd)
   2636       {
   2637       case VMC_VEND_REQUEST:
   2638         {
   2639           unsigned int product;
   2640 
   2641           /* Calculate the checksum and check it */
   2642           chkSum = cmd;
   2643 
   2644           for (size_t offset = 1; offset < ((hex_len / 2)); offset++)
   2645           {
   2646             chkSum += tmp;
   2647             if (1 != sscanf (hex + (2 * offset),
   2648                              "%2X",
   2649                              &tmp))
   2650             {
   2651               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2652                           "Received non-HEX input `%.*s'\n",
   2653                           (int) hex_len,
   2654                           hex);
   2655               GNUNET_break_op (0);
   2656               return;
   2657             }
   2658           }
   2659           if ( ((uint8_t) (chkSum & 0xFF)) != tmp)
   2660           {
   2661             mdb.cmd = &cmd_reader_display_internal_error;
   2662             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2663                         "Received command with wrong checksum `%.*s'\n",
   2664                         (int) hex_len,
   2665                         hex);
   2666             temporary_error ("err-num-read-fail");
   2667             break;
   2668           }
   2669 
   2670           GNUNET_break (mdb.session_running);
   2671           /* NOTE: hex[4..7] contain the price */
   2672           if (12 > hex_len)
   2673           {
   2674             GNUNET_break_op (0);
   2675             return;
   2676           }
   2677           if (1 != sscanf (&hex[8],
   2678                            "%4X",
   2679                            &product))
   2680           {
   2681             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2682                         "Received non-HEX input `%.*s'\n",
   2683                         (int) hex_len - 8,
   2684                         &hex[8]);
   2685             GNUNET_break_op (0);
   2686             return;
   2687           }
   2688           /* compare the received product number with the defined product numbers */
   2689           for (unsigned int i = 0; i < products_length; i++)
   2690             if (product == products[i].number)
   2691             {
   2692               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2693                           "Product %u selected on MDB\n",
   2694                           product);
   2695               if ( (sold_out_enabled) &&
   2696                    (products[i].sold_out) )
   2697               {
   2698                 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2699                             "Product %s sold out, denying vend\n",
   2700                             products[i].description);
   2701                 mdb.cmd = &cmd_reader_display_sold_out;
   2702                 temporary_error ("err-sold-out");
   2703                 return;
   2704               }
   2705               payment_activity = launch_payment (&products[i]);
   2706               if (NULL == payment_activity)
   2707                 vend_failure ();
   2708               return;
   2709             }
   2710           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2711                       "Unknown product %u selected on MDB, denying vend\n",
   2712                       product);
   2713           temporary_error ("unknown-product");
   2714           mdb.cmd = &cmd_deny_vend;
   2715           break;
   2716         }
   2717       case VMC_VEND_SUCCESS:
   2718         GNUNET_break (mdb.session_running);
   2719         vend_success ();
   2720         break;
   2721       case VMC_VEND_FAILURE:
   2722         {
   2723           GNUNET_break (mdb.session_running);
   2724           vend_failure ();
   2725           break;
   2726         }
   2727       case VMC_VEND_SESSION_COMPLETE:
   2728         {
   2729           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2730                       "Received MDB session complete\n");
   2731           mdb.session_running = false;
   2732           mdb.cmd = &endSession;
   2733           if (NULL != payment_activity)
   2734           {
   2735             cleanup_payment (payment_activity);
   2736             payment_activity = NULL;
   2737           }
   2738         }
   2739         break;
   2740       default:
   2741         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2742                     "Unknown MDB sub-command %X of command %X\n",
   2743                     subcmd,
   2744                     cmd);
   2745         break;
   2746       }
   2747       break;
   2748     }
   2749   case VMC_REVALUE:
   2750     {
   2751       unsigned int subcmd;
   2752 
   2753       if (4 > hex_len)
   2754       {
   2755         GNUNET_break_op (0);
   2756         return;
   2757       }
   2758       if (1 != sscanf (&hex[2],
   2759                        "%2X",
   2760                        &subcmd))
   2761       {
   2762         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2763                     "Received non-HEX input `%.*s'\n",
   2764                     (int) hex_len - 2,
   2765                     &hex[2]);
   2766         GNUNET_break_op (0);
   2767         return;
   2768       }
   2769       switch (subcmd)
   2770       {
   2771       case VMC_REVALUE_REQUEST:
   2772         {
   2773           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2774                       "Received request for revalue via MDB\n");
   2775           mdb.cmd = &cmd_revalue_approved;
   2776           break;
   2777         }
   2778       case VMC_REVALUE_LIMIT_REQUEST:
   2779         {
   2780           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2781                       "Received request for revalue limit amount via MDB\n");
   2782           mdb.cmd = &cmd_revalue_amount;
   2783           break;
   2784         }
   2785       default:
   2786         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2787                     "Unknown MDB sub-command %X of command %X\n",
   2788                     subcmd,
   2789                     cmd);
   2790         break;
   2791       }
   2792       break;
   2793     }
   2794   case VMC_CONF:
   2795     {
   2796       unsigned int subcmd;
   2797 
   2798       if (4 > hex_len)
   2799       {
   2800         GNUNET_break_op (0);
   2801         return;
   2802       }
   2803       if (1 != sscanf (&hex[2],
   2804                        "%2X",
   2805                        &subcmd))
   2806       {
   2807         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2808                     "Received non-HEX input `%.*s'\n",
   2809                     (int) hex_len - 2,
   2810                     &hex[2]);
   2811         GNUNET_break_op (0);
   2812         return;
   2813       }
   2814       switch (subcmd)
   2815       {
   2816       case VMC_READER_CONF:
   2817         {
   2818           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2819                       "Received request for configuration via MDB\n");
   2820           mdb.cmd = &cmd_reader_config_data;
   2821           break;
   2822 
   2823         }
   2824       case VMC_SETUP_MAX_MIN_PRICES:
   2825         {
   2826           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2827                       "Received max and min prices via MDB\n");
   2828           break;
   2829         }
   2830       default:
   2831         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2832                     "Unknown MDB sub-command %X of command %X\n",
   2833                     subcmd,
   2834                     cmd);
   2835         break;
   2836       }
   2837       break;
   2838     }
   2839   case VMC_POLL:
   2840     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2841                 "Received POLL from MDB (ignored)\n");
   2842     break;
   2843   case VMC_CMD_RESET:
   2844     {
   2845       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2846                   "Received RESET from MDB (ignored)\n");
   2847       break;
   2848     }
   2849   case VMC_READER:
   2850     {
   2851       unsigned int subcmd;
   2852 
   2853       if (4 > hex_len)
   2854       {
   2855         GNUNET_break_op (0);
   2856         return;
   2857       }
   2858       if (1 != sscanf (&hex[2],
   2859                        "%2X",
   2860                        &subcmd))
   2861       {
   2862         GNUNET_break_op (0);
   2863         return;
   2864       }
   2865 
   2866       switch (subcmd)
   2867       {
   2868       case VMC_READER_DISABLE:
   2869         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2870                     "Received Reader Disable via MDB\n");
   2871         mdb.session_running = false;
   2872         if (NULL != payment_activity)
   2873         {
   2874           cleanup_payment (payment_activity);
   2875           payment_activity = NULL;
   2876         }
   2877         for (unsigned int i = 0; i < products_length; i++)
   2878         {
   2879           if ( (sold_out_enabled) &&
   2880                (0 != strcmp (products[i].description,
   2881                              "empty")) &&
   2882                (products[i].sold_out) )
   2883           {
   2884             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2885                         "Resetting sold out state of product %s\n",
   2886                         products[i].description);
   2887             products[i].sold_out = false;
   2888           }
   2889         }
   2890         break;
   2891       case VMC_READER_ENABLE:
   2892         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2893                     "Received Reader Enable via MDB\n");
   2894         mdb.session_running = false;
   2895         break;
   2896       case VMC_READER_CANCEL:
   2897         mdb.cmd = &cmd_reader_cancelled;
   2898         mdb.session_running = false;
   2899         break;
   2900       default:
   2901         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2902                     "Unknown MDB sub-command %X of command %X\n",
   2903                     subcmd,
   2904                     cmd);
   2905         break;
   2906       }
   2907       break;
   2908     }
   2909   case VMC_REQUEST_ID:
   2910     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2911                 "Received VMC request ID, no need to handle (done by HW)\n");
   2912     break;
   2913   case VMC_ACKN:
   2914     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2915                 "Received acknowledgement (for command `%s') from MDB\n",
   2916                 (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
   2917     handle_ack ();
   2918     break;
   2919   case VMC_OOSQ:
   2920     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2921                 "Received command out of sequence from MDB (for command `%s')\n",
   2922                 (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
   2923     mdb.session_running = false;
   2924     if (mdb.last_cmd != &endSession)
   2925       mdb.cmd = &endSession;
   2926     else
   2927       mdb.last_cmd = NULL;
   2928     break;
   2929   case VMC_RETR:
   2930     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2931                 "Received request to resend previous data from MDB (previous command was `%s')\n",
   2932                 (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
   2933     GNUNET_break (NULL == mdb.cmd);
   2934     GNUNET_break (NULL != mdb.last_cmd);
   2935     mdb.cmd = mdb.last_cmd;
   2936     mdb.last_cmd = NULL;
   2937     break;
   2938   default:
   2939     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2940                 "Received unknown MDB command %X\n",
   2941                 cmd);
   2942     break;
   2943   }
   2944 }
   2945 
   2946 
   2947 /**
   2948  * @brief Read in the sent commands of the VMC controller
   2949  *
   2950  * @param cls closure
   2951  */
   2952 static void
   2953 read_mdb_command (void *cls)
   2954 {
   2955   ssize_t ret;
   2956   size_t cmdStartIdx;
   2957   size_t cmdEndIdx;
   2958 
   2959   (void) cls;
   2960   /* don't read if the mdb bus is disabled (only for testing) */
   2961   GNUNET_assert (! disable_mdb);
   2962   mdb.rtask = NULL;
   2963   ret = read (mdb.uartfd,
   2964               &mdb.rxBuffer[mdb.rx_off],
   2965               sizeof (mdb.rxBuffer) - mdb.rx_off);
   2966   if (-1 == ret)
   2967   {
   2968     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   2969                               "read",
   2970                               uart_device_filename);
   2971     GNUNET_SCHEDULER_shutdown ();
   2972     return;
   2973   }
   2974   mdb.rx_off += ret;
   2975 
   2976   while (mdb.rx_off > 0)
   2977   {
   2978     mdb_active = true;
   2979     /* find beginning of command */
   2980     for (cmdStartIdx = 0; cmdStartIdx < mdb.rx_off; cmdStartIdx++)
   2981       if (VMC_CMD_START == mdb.rxBuffer[cmdStartIdx])
   2982         break;
   2983     if (cmdStartIdx == mdb.rx_off)
   2984     {
   2985       mdb.rx_off = 0;
   2986       break;
   2987     }
   2988     /* find end of command */
   2989     for (cmdEndIdx = cmdStartIdx; cmdEndIdx < mdb.rx_off; cmdEndIdx++)
   2990       if (VMC_CMD_END == mdb.rxBuffer[cmdEndIdx])
   2991         break;
   2992     if (cmdEndIdx == mdb.rx_off)
   2993     {
   2994       /* check to make sure rxBuffer was big enough in principle */
   2995       if ( (cmdStartIdx == 0) &&
   2996            (mdb.rx_off == sizeof (mdb.rxBuffer)) )
   2997       {
   2998         /* Developer: if this happens, try increasing rxBuffer! */
   2999         GNUNET_break (0);
   3000         GNUNET_SCHEDULER_shutdown ();
   3001         return;
   3002       }
   3003       /* move cmd in buffer to the beginning of the buffer */
   3004       memmove (mdb.rxBuffer,
   3005                &mdb.rxBuffer[cmdStartIdx],
   3006                mdb.rx_off - cmdStartIdx);
   3007       mdb.rx_off -= cmdStartIdx;
   3008       break;
   3009     }
   3010     /* if the full command was received parse it */
   3011     handle_command ((const char *) &mdb.rxBuffer[cmdStartIdx + 1],
   3012                     cmdEndIdx - cmdStartIdx - 1);
   3013     /* move the data after the processed command to the left */
   3014     memmove (mdb.rxBuffer,
   3015              &mdb.rxBuffer[cmdEndIdx + 1],
   3016              mdb.rx_off - cmdEndIdx + 1);
   3017     mdb.rx_off -= (cmdEndIdx + 1);
   3018   }
   3019   if (in_shutdown)
   3020   {
   3021     mdb_shutdown ();
   3022     return;
   3023   }
   3024   run_mdb_event_loop ();
   3025 }
   3026 
   3027 
   3028 /**
   3029  * @brief Mdb event loop to start read and write tasks
   3030  */
   3031 static void
   3032 run_mdb_event_loop (void)
   3033 {
   3034   struct GNUNET_DISK_FileHandle fh = { mdb.uartfd };
   3035 
   3036   /* begin session if no cmd waits for sending and no cmd is received from the VMC */
   3037   if ( (! mdb.session_running) &&
   3038        (NULL == mdb.cmd) &&
   3039        (NULL == mdb.last_cmd) )
   3040   {
   3041     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3042                 "Beginning MDB session\n");
   3043     mdb.cmd = &cmd_begin_session;
   3044   }
   3045   /* start write task if he doesn't exist and if there is a cmd waiting to get sent */
   3046   if ( (NULL == mdb.wtask) &&
   3047        ( (NULL != mdb.cmd) ||
   3048          (in_shutdown) ||
   3049          (mdb.tx_len > mdb.tx_off) ) )
   3050   {
   3051     if (disable_mdb || ! mdb_active)
   3052       mdb.wtask = GNUNET_SCHEDULER_add_now (&write_mdb_command,
   3053                                             NULL);
   3054     else
   3055       mdb.wtask = GNUNET_SCHEDULER_add_write_file ((in_shutdown)
   3056                                                    ? SHUTDOWN_MDB_TIMEOUT
   3057                                                    :
   3058                                                    GNUNET_TIME_UNIT_FOREVER_REL,
   3059                                                    &fh,
   3060                                                    &write_mdb_command,
   3061                                                    NULL);
   3062   }
   3063   if ( (disable_mdb) &&
   3064        (NULL != mdb.last_cmd) )
   3065   {
   3066     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3067                 "Faking acknowledgement (for command `%s') from MDB\n",
   3068                 mdb.last_cmd->name);
   3069     handle_ack ();
   3070   }
   3071   /* start read task if he doesn't exist and the mdb communication is not disabled (only for testing) */
   3072   if ( (NULL == mdb.rtask) &&
   3073        (! disable_mdb) )
   3074     mdb.rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
   3075                                                 &fh,
   3076                                                 &read_mdb_command,
   3077                                                 NULL);
   3078 }
   3079 
   3080 
   3081 /**
   3082  * @brief Read the products from the configuration file
   3083  *
   3084  * @param cls closure in this case the name of the configuration file to read from
   3085  * @param section section of the config file to read from
   3086  */
   3087 static void
   3088 read_products (void *cls,
   3089                const char *section)
   3090 {
   3091   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   3092   struct Product tmpProduct = {
   3093     .sold_out = false
   3094   };
   3095   char *tmpKey;
   3096   char *thumbnail_fn;
   3097 
   3098   /* if the current section is not a product skip it */
   3099   if (0 != strncmp (section,
   3100                     "product-",
   3101                     strlen ("product-")))
   3102     return;
   3103   /* the current section is a product, parse its specifications and store it in a temporary product */
   3104   if (GNUNET_OK !=
   3105       GNUNET_CONFIGURATION_get_value_string (cfg,
   3106                                              section,
   3107                                              "DESCRIPTION",
   3108                                              &tmpProduct.description))
   3109   {
   3110     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3111                                section,
   3112                                "DESCRIPTION");
   3113     return;
   3114   }
   3115   if (sold_out_enabled)
   3116   {
   3117     if (0 == strcmp (tmpProduct.description,
   3118                      "empty"))
   3119     {
   3120       tmpProduct.sold_out = true;
   3121     }
   3122     else
   3123     {
   3124       tmpProduct.sold_out = false;
   3125     }
   3126   }
   3127   if (GNUNET_OK !=
   3128       TALER_config_get_amount (cfg,
   3129                                section,
   3130                                "price",
   3131                                &tmpProduct.price))
   3132   {
   3133     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3134                                section,
   3135                                "price");
   3136     GNUNET_free (tmpProduct.description);
   3137     return;
   3138   }
   3139   if (GNUNET_OK ==
   3140       GNUNET_CONFIGURATION_get_value_string (cfg,
   3141                                              section,
   3142                                              "KEY",
   3143                                              &tmpKey))
   3144   {
   3145     tmpProduct.key = tmpKey[0];
   3146     GNUNET_free (tmpKey);
   3147   }
   3148   else
   3149   {
   3150     /* no key */
   3151     tmpProduct.key = '\0';
   3152   }
   3153   if (GNUNET_OK !=
   3154       GNUNET_CONFIGURATION_get_value_string (cfg,
   3155                                              section,
   3156                                              "INSTANCE",
   3157                                              &tmpProduct.instance))
   3158     tmpProduct.instance = NULL;
   3159   tmpProduct.preview = NULL;
   3160   if (GNUNET_OK ==
   3161       GNUNET_CONFIGURATION_get_value_string (cfg,
   3162                                              section,
   3163                                              "THUMBNAIL",
   3164                                              &thumbnail_fn))
   3165   {
   3166     struct GNUNET_DISK_FileHandle *fh;
   3167 
   3168     fh = GNUNET_DISK_file_open (thumbnail_fn,
   3169                                 GNUNET_DISK_OPEN_READ,
   3170                                 GNUNET_DISK_PERM_NONE);
   3171     if (NULL == fh)
   3172     {
   3173       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3174                   "Could not open thumbnail `%s'\n",
   3175                   thumbnail_fn);
   3176     }
   3177     else
   3178     {
   3179       off_t flen;
   3180 
   3181       if (GNUNET_OK !=
   3182           GNUNET_DISK_file_handle_size (fh,
   3183                                         &flen))
   3184       {
   3185         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   3186                                   "stat",
   3187                                   thumbnail_fn);
   3188       }
   3189       else
   3190       {
   3191         void *thumb;
   3192         size_t len;
   3193         ssize_t ret;
   3194 
   3195         len = (size_t) flen;
   3196         thumb = GNUNET_malloc (len);
   3197         ret = GNUNET_DISK_file_read (fh,
   3198                                      thumb,
   3199                                      len);
   3200         if ( (ret < 0) ||
   3201              (len != ((size_t) ret)) )
   3202         {
   3203           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   3204                                     "read",
   3205                                     thumbnail_fn);
   3206         }
   3207         else
   3208         {
   3209           const char *mime_type = "";
   3210           const char *ext;
   3211           char *base64;
   3212 
   3213           ext = strrchr (thumbnail_fn, '.');
   3214           if (NULL != ext)
   3215           {
   3216             static const struct
   3217             {
   3218               const char *ext;
   3219               const char *mime;
   3220             } mimes[] = {
   3221               { ".png", "image/png" },
   3222               { ".jpg", "image/jpeg" },
   3223               { ".jpeg", "image/jpeg" },
   3224               { ".svg", "image/svg" },
   3225               { NULL, NULL }
   3226             };
   3227 
   3228             for (unsigned int i = 0; NULL != mimes[i].ext; i++)
   3229               if (0 == strcasecmp (mimes[i].ext,
   3230                                    ext))
   3231               {
   3232                 mime_type = mimes[i].mime;
   3233                 break;
   3234               }
   3235           }
   3236           (void) GNUNET_STRINGS_base64_encode (thumb,
   3237                                                len,
   3238                                                &base64);
   3239           GNUNET_asprintf (&tmpProduct.preview,
   3240                            "data:%s;base64,%s",
   3241                            mime_type,
   3242                            base64);
   3243           GNUNET_free (base64);
   3244         }
   3245         GNUNET_free (thumb);
   3246       }
   3247       GNUNET_break (GNUNET_OK ==
   3248                     GNUNET_DISK_file_close (fh));
   3249     }
   3250     GNUNET_free (thumbnail_fn);
   3251   }
   3252   if (GNUNET_OK !=
   3253       GNUNET_CONFIGURATION_get_value_number (cfg,
   3254                                              section,
   3255                                              "NUMBER",
   3256                                              &tmpProduct.number))
   3257   {
   3258     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3259                                section,
   3260                                "NUMBER");
   3261     GNUNET_free (tmpProduct.description);
   3262     GNUNET_free (tmpProduct.instance);
   3263     GNUNET_free (tmpProduct.preview);
   3264     return;
   3265   }
   3266 
   3267   {
   3268     char *auth;
   3269 
   3270     if ( (GNUNET_OK !=
   3271           GNUNET_CONFIGURATION_get_value_string (cfg,
   3272                                                  section,
   3273                                                  "BACKEND_AUTHORIZATION",
   3274                                                  &auth)) &&
   3275          (GNUNET_OK !=
   3276           GNUNET_CONFIGURATION_get_value_string (cfg,
   3277                                                  "taler-mdb",
   3278                                                  "BACKEND_AUTHORIZATION",
   3279                                                  &auth)) )
   3280     {
   3281       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3282                                  section,
   3283                                  "BACKEND_AUTHORIZATION");
   3284       GNUNET_free (tmpProduct.description);
   3285       GNUNET_free (tmpProduct.instance);
   3286       GNUNET_free (tmpProduct.preview);
   3287       return;
   3288     }
   3289     GNUNET_asprintf (&tmpProduct.auth_header,
   3290                      "%s: %s",
   3291                      MHD_HTTP_HEADER_AUTHORIZATION,
   3292                      auth);
   3293     GNUNET_free (auth);
   3294   }
   3295   /* append the temporary product to the existing products */
   3296   GNUNET_array_append (products,
   3297                        products_length,
   3298                        tmpProduct);
   3299 }
   3300 
   3301 
   3302 /**
   3303  * @brief Initialise the uart device to send mdb commands
   3304  *
   3305  * @return #GNUNET_OK on success
   3306  */
   3307 static enum GNUNET_GenericReturnValue
   3308 mdb_init (void)
   3309 {
   3310   struct termios uart_opts_raw;
   3311 
   3312   if (disable_mdb)
   3313   {
   3314     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3315                 "Running with MDB disabled!\n");
   3316     run_mdb_event_loop ();
   3317     return GNUNET_OK;
   3318   }
   3319   /* open uart connection */
   3320   if (0 > (mdb.uartfd = open (uart_device_filename,
   3321                               O_RDWR | O_NOCTTY | O_NDELAY)))
   3322   {
   3323     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   3324                               "open",
   3325                               uart_device_filename);
   3326     return GNUNET_SYSERR;
   3327   }
   3328 
   3329   if (0 != tcgetattr (mdb.uartfd,
   3330                       &mdb.uart_opts_backup))
   3331   {
   3332     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
   3333                          "tcgetattr");
   3334     return GNUNET_SYSERR;
   3335   }
   3336   uart_opts_raw = mdb.uart_opts_backup;
   3337 
   3338   /* reset current uart discipline */
   3339   memset (&uart_opts_raw,
   3340           0,
   3341           sizeof(uart_opts_raw));
   3342 
   3343   /* set baudrate */
   3344   cfsetispeed (&uart_opts_raw, B9600);
   3345   cfsetospeed (&uart_opts_raw, B9600);
   3346 
   3347   /* set options */
   3348   uart_opts_raw.c_cflag &= ~PARENB;
   3349   uart_opts_raw.c_cflag &= ~CSTOPB;
   3350   uart_opts_raw.c_cflag &= ~CSIZE;
   3351   uart_opts_raw.c_cflag |= CS8;
   3352 
   3353   /* 19200 bps, 8 databits, ignore cd-signal, allow reading */
   3354   uart_opts_raw.c_cflag |= (CLOCAL | CREAD);
   3355   uart_opts_raw.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
   3356   uart_opts_raw.c_iflag = IGNPAR;
   3357   uart_opts_raw.c_oflag &= ~OPOST;
   3358   uart_opts_raw.c_cc[VMIN]  = 0;
   3359   uart_opts_raw.c_cc[VTIME] = 50;
   3360   tcflush (mdb.uartfd, TCIOFLUSH);
   3361   if (0 != tcsetattr (mdb.uartfd,
   3362                       TCSAFLUSH,
   3363                       &uart_opts_raw))
   3364   {
   3365     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
   3366                          "tcsetattr");
   3367     return GNUNET_SYSERR;
   3368   }
   3369   /* if the configuration of the uart was successful start the mdb write and read tasks */
   3370   run_mdb_event_loop ();
   3371   return GNUNET_OK;
   3372 }
   3373 
   3374 
   3375 /**
   3376  * @brief Start the application
   3377  *
   3378  * @param cls closure
   3379  * @param args arguments left
   3380  * @param cfgfile config file name
   3381  * @param cfg handle for the configuration
   3382  */
   3383 static void
   3384 run (void *cls,
   3385      char *const *args,
   3386      const char *cfgfile,
   3387      const struct GNUNET_CONFIGURATION_Handle *cfg)
   3388 {
   3389   (void) cls;
   3390   (void) args;
   3391   (void) cfgfile;
   3392 
   3393   /* parse the devices, if no config entry is found, a standard is used */
   3394   if (GNUNET_OK !=
   3395       GNUNET_CONFIGURATION_get_value_filename (cfg,
   3396                                                "taler-mdb",
   3397                                                "UART_DEVICE",
   3398                                                &uart_device_filename))
   3399   {
   3400     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3401                                "taler-mdb",
   3402                                "UART_DEVICE");
   3403     uart_device_filename = GNUNET_strdup ("/dev/ttyAMA0");
   3404   }
   3405   if (GNUNET_OK !=
   3406       GNUNET_CONFIGURATION_get_value_filename (cfg,
   3407                                                "taler-mdb",
   3408                                                "FRAMEBUFFER_DEVICE",
   3409                                                &framebuffer_device_filename))
   3410   {
   3411     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
   3412                                "taler-mdb",
   3413                                "FRAMEBUFFER_DEVICE");
   3414     framebuffer_device_filename = GNUNET_strdup ("/dev/fb1");
   3415   }
   3416   if (GNUNET_OK !=
   3417       GNUNET_CONFIGURATION_get_value_filename (cfg,
   3418                                                "taler-mdb",
   3419                                                "FRAMEBUFFER_BACKLIGHT",
   3420                                                &framebuffer_backlight_filename))
   3421   {
   3422     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3423                                "taler-mdb",
   3424                                "FRAMEBUFFER_BACKLIGHT");
   3425     framebuffer_backlight_filename = GNUNET_strdup (
   3426       "/sys/class/backlight/soc:backlight/brightness");
   3427   }
   3428   /* parse the taler configurations */
   3429   if (GNUNET_OK !=
   3430       GNUNET_CONFIGURATION_get_value_string (cfg,
   3431                                              "taler-mdb",
   3432                                              "BACKEND_BASE_URL",
   3433                                              &backend_base_url))
   3434   {
   3435     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3436                                "taler-mdb",
   3437                                "BACKEND_BASE_URL");
   3438     global_ret = EXIT_FAILURE;
   3439     return;
   3440   }
   3441   (void) GNUNET_CONFIGURATION_get_value_string (cfg,
   3442                                                 "taler-mdb",
   3443                                                 "ADVERTISEMENT_COMMAND",
   3444                                                 &adv_process_command);
   3445   (void) GNUNET_CONFIGURATION_get_value_string (cfg,
   3446                                                 "taler-mdb",
   3447                                                 "FAIL_COMMAND",
   3448                                                 &err_process_command);
   3449   if (GNUNET_OK !=
   3450       GNUNET_CONFIGURATION_get_value_string (cfg,
   3451                                              "taler-mdb",
   3452                                              "FULFILLMENT_MSG",
   3453                                              &fulfillment_msg))
   3454   {
   3455     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3456                                "taler-mdb",
   3457                                "FULFILLMENT_MSG");
   3458     global_ret = EXIT_FAILURE;
   3459     return;
   3460   }
   3461 
   3462   /* parse the products */
   3463   GNUNET_CONFIGURATION_iterate_sections (cfg,
   3464                                          &read_products,
   3465                                          (void *) cfg);
   3466   if (NULL == products)
   3467   {
   3468     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3469                 "No valid products configured\n");
   3470     global_ret = EXIT_NOTCONFIGURED;
   3471     return;
   3472   }
   3473 
   3474   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
   3475                                  NULL);
   3476 
   3477   /* initialize mdb */
   3478   if (GNUNET_OK != mdb_init ())
   3479   {
   3480     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3481                 "Unable to initialize MDB (mdb_init() failed)\n");
   3482     global_ret = EXIT_FAILURE;
   3483     GNUNET_SCHEDULER_shutdown ();
   3484     return;
   3485   }
   3486 
   3487   /* initialize nfc */
   3488   nfc_init (&context);
   3489   if (NULL == context)
   3490   {
   3491     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3492                 "Unable to initialize NFC (nfc_init() failed)\n");
   3493     global_ret = EXIT_FAILURE;
   3494     GNUNET_SCHEDULER_shutdown ();
   3495     return;
   3496   }
   3497 
   3498   /* open gpio pin for cancel button */
   3499   {
   3500     int efd;
   3501 
   3502     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3503                 "Exporting GPIO pin 23\n");
   3504     efd = open ("/sys/class/gpio/export",
   3505                 O_WRONLY);
   3506     if (-1 == efd)
   3507     {
   3508       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3509                   "Unable to open /gpio/export for cancel button\n");
   3510     }
   3511     else
   3512     {
   3513       if (2 != write (efd,
   3514                       "23",
   3515                       2))
   3516       {
   3517         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
   3518                                   "write",
   3519                                   "/sys/class/gpio/export");
   3520       }
   3521       GNUNET_assert (0 == close (efd));
   3522     }
   3523   }
   3524 
   3525   /* set direction: input */
   3526   {
   3527     int dfd;
   3528 
   3529     dfd = open ("/sys/class/gpio/gpio23/direction",
   3530                 O_WRONLY);
   3531     if (-1 == dfd)
   3532     {
   3533       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3534                   "Unable to open /gpio/gpio23/direction for cancel button\n");
   3535     }
   3536     else
   3537     {
   3538       if (2 != write (dfd,
   3539                       "in",
   3540                       2))
   3541       {
   3542         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
   3543                                   "write",
   3544                                   "/sys/class/gpio/gpio23/direction");
   3545       }
   3546       GNUNET_assert (0 == close (dfd));
   3547     }
   3548   }
   3549 
   3550   {
   3551     /* actually open fd for reading the state */
   3552     cancel_button.cancelbuttonfd = open ("/sys/class/gpio/gpio23/value",
   3553                                          O_RDONLY);
   3554     if (-1 == cancel_button.cancelbuttonfd)
   3555     {
   3556       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3557                   "Unable to open /gpio/gpio23/value for cancel button\n");
   3558     }
   3559   }
   3560 
   3561 
   3562 #if HAVE_QRENCODE_H
   3563   /* open the framebuffer device */
   3564   qrDisplay.devicefd = open (framebuffer_device_filename,
   3565                              O_RDWR);
   3566   if (-1 != qrDisplay.devicefd)
   3567   {
   3568     /* read information about the screen */
   3569     ioctl (qrDisplay.devicefd,
   3570            FBIOGET_VSCREENINFO,
   3571            &qrDisplay.var_info);
   3572 
   3573     /* store current screeninfo for reset */
   3574     qrDisplay.orig_vinfo = qrDisplay.var_info;
   3575 
   3576     if (16 != qrDisplay.var_info.bits_per_pixel)
   3577     {
   3578       /* Change variable info to 16 bit per pixel */
   3579       qrDisplay.var_info.bits_per_pixel = 16;
   3580       if (0 > ioctl (qrDisplay.devicefd,
   3581                      FBIOPUT_VSCREENINFO,
   3582                      &qrDisplay.var_info))
   3583       {
   3584         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   3585                              "ioctl(FBIOPUT_VSCREENINFO)");
   3586         return;
   3587       }
   3588     }
   3589 
   3590     /* Get fixed screen information */
   3591     if (0 > ioctl (qrDisplay.devicefd,
   3592                    FBIOGET_FSCREENINFO,
   3593                    &qrDisplay.fix_info))
   3594     {
   3595       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   3596                            "ioctl(FBIOGET_FSCREENINFO)");
   3597       return;
   3598     }
   3599 
   3600     /* get pointer onto frame buffer */
   3601     qrDisplay.memory = mmap (NULL,
   3602                              qrDisplay.fix_info.smem_len,
   3603                              PROT_READ | PROT_WRITE, MAP_SHARED,
   3604                              qrDisplay.devicefd,
   3605                              0);
   3606     if (MAP_FAILED == qrDisplay.memory)
   3607     {
   3608       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   3609                            "mmap");
   3610       return;
   3611     }
   3612 
   3613     /* set the screen to white */
   3614     memset (qrDisplay.memory,
   3615             0xFF,
   3616             qrDisplay.var_info.xres * qrDisplay.var_info.yres
   3617             * sizeof (uint16_t));
   3618 
   3619     /* open backlight file to turn display backlight on and off */
   3620     qrDisplay.backlightfd = open (
   3621       framebuffer_backlight_filename, O_WRONLY);
   3622     if (0 > qrDisplay.backlightfd)
   3623     {
   3624       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
   3625                                 "open",
   3626                                 framebuffer_backlight_filename);
   3627     }
   3628     else
   3629     {
   3630       /* if '-i' flag was set, invert the on and off values */
   3631       if (backlight_invert)
   3632       {
   3633         backlight_on = '0';
   3634         backlight_off = '1';
   3635       }
   3636       /* turn off the backlight */
   3637       (void) ! write (qrDisplay.backlightfd,
   3638                       &backlight_off,
   3639                       1);
   3640     }
   3641   }
   3642   else
   3643   {
   3644     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
   3645                               "open",
   3646                               framebuffer_device_filename);
   3647   }
   3648 #endif
   3649   start_advertising ();
   3650   if (! disable_tty)
   3651     start_read_keyboard ();
   3652 }
   3653 
   3654 
   3655 /**
   3656  * @brief Convert the ASCII cmd in @hex to hex and store it in the mdb data struct @blk
   3657  *
   3658  * @param *blk pointer with reference to the mdb datablock
   3659  * @param *hex pointer to the string containing the cmd data
   3660  */
   3661 static void
   3662 parse_block (struct MdbBlock *blk,
   3663              const char *hex)
   3664 {
   3665   if (NULL == hex)
   3666   {
   3667     blk->bin_size = 0;
   3668     blk->bin = NULL;
   3669     return;
   3670   }
   3671   blk->bin_size = strlen (hex) / 2;
   3672   blk->bin = GNUNET_malloc (blk->bin_size);
   3673   for (size_t idx = 0; idx < blk->bin_size; idx++)
   3674   {
   3675     unsigned int val;
   3676 
   3677     GNUNET_assert (1 ==
   3678                    sscanf (&hex[idx * 2],
   3679                            "%2X",
   3680                            &val));
   3681     blk->bin[idx] = (uint8_t) val;
   3682   }
   3683 }
   3684 
   3685 
   3686 /**
   3687  * @brief Create a new mdb command
   3688  *
   3689  * @param *name pointer to the string containing the command name
   3690  * @param *cmd pointer to the string containing the command
   3691  * @param *data pointer to the string containing the command data
   3692  * @return structure of type MdbCommand holding the given information by the parameters
   3693  */
   3694 static struct MdbCommand
   3695 setup_mdb_cmd (const char *name,
   3696                const char *cmd,
   3697                const char *data)
   3698 {
   3699   struct MdbCommand cmdNew;
   3700 
   3701   cmdNew.name = (NULL == name)
   3702                 ? "No Cmd Name Set"
   3703                 : name;
   3704   parse_block (&cmdNew.cmd, cmd);
   3705   parse_block (&cmdNew.data, data);
   3706   return cmdNew;
   3707 }
   3708 
   3709 
   3710 int
   3711 main (int argc,
   3712       char*const*argv)
   3713 {
   3714   struct termios tty_opts_backup;
   3715   struct termios tty_opts_raw;
   3716   int have_tty;
   3717   int ret;
   3718   struct GNUNET_GETOPT_CommandLineOption options[] = {
   3719     GNUNET_GETOPT_option_flag ('d',
   3720                                "disable-mdb",
   3721                                "disable all interactions with the MDB (for testing without machine)",
   3722                                &disable_mdb),
   3723     GNUNET_GETOPT_option_flag ('i',
   3724                                "backlight-invert",
   3725                                "invert the backlight on/off values (standard on = 1)",
   3726                                &backlight_invert),
   3727     GNUNET_GETOPT_option_flag ('s',
   3728                                "enable-soldout",
   3729                                "enable detection of sold-out products, preventing vend operations of the respective product until the process is restarted",
   3730                                &sold_out_enabled),
   3731     GNUNET_GETOPT_option_flag ('t',
   3732                                "disable-tty",
   3733                                "disable all keyboard interactions (for running from systemd)",
   3734                                &disable_tty),
   3735     GNUNET_GETOPT_OPTION_END
   3736   };
   3737 
   3738   if (! disable_tty)
   3739   {
   3740     have_tty = isatty (STDIN_FILENO);
   3741     if (have_tty)
   3742     {
   3743       if (0 != tcgetattr (STDIN_FILENO, &tty_opts_backup))
   3744         fprintf (stderr,
   3745                  "Failed to get terminal discipline\n");
   3746       tty_opts_raw = tty_opts_backup;
   3747       tty_opts_raw.c_lflag &= ~(ECHO | ECHONL | ICANON);
   3748       if (0 != tcsetattr (STDIN_FILENO, TCSANOW, &tty_opts_raw))
   3749         fprintf (stderr,
   3750                  "Failed to set terminal discipline\n");
   3751     }
   3752   }
   3753   /* make the needed commands for the communication with the vending machine controller */
   3754   cmd_reader_config_data = setup_mdb_cmd ("Reader Config",
   3755                                           READER_CONFIG,
   3756                                           READER_FEATURE_LEVEL
   3757                                           READER_COUNTRYCODE
   3758                                           READER_SCALE_FACTOR
   3759                                           READER_DECIMAL_PLACES
   3760                                           READER_MAX_RESPONSE_TIME
   3761                                           READER_MISC_OPTIONS);
   3762   cmd_begin_session = setup_mdb_cmd ("Begin Session",
   3763                                      READER_BEGIN_SESSION,
   3764                                      READER_FUNDS_AVAILABLE);
   3765   cmd_approve_vend = setup_mdb_cmd ("Approve Vend",
   3766                                     READER_VEND_APPROVE,
   3767                                     READER_VEND_AMOUNT);
   3768   cmd_reader_cancelled = setup_mdb_cmd ("Confirm cancellation",
   3769                                         READER_CANCELLED,
   3770                                         NULL);
   3771   cmd_deny_vend = setup_mdb_cmd ("Deny Vend",
   3772                                  READER_VEND_DENIED,
   3773                                  NULL);
   3774   endSession = setup_mdb_cmd ("End Session",
   3775                               READER_END_SESSION,
   3776                               NULL);
   3777   cmd_revalue_approved = setup_mdb_cmd ("Reader Approve Revalue",
   3778                                         READER_REVALUE_APPROVED,
   3779                                         NULL);
   3780 
   3781   cmd_revalue_amount = setup_mdb_cmd ("Send Revalue Limit Amount",
   3782                                       READER_REVALUE_LIMIT,
   3783                                       READER_REVALUE_LIMIT_AMOUNT);
   3784 
   3785   cmd_reader_NACK = setup_mdb_cmd ("Reader NACK",
   3786                                    READER_NACK,
   3787                                    NULL);
   3788 
   3789   cmd_reader_display_sold_out = setup_mdb_cmd ("Display Sold Out",
   3790                                                READER_DISPLAY_REQUEST,
   3791                                                READER_DISPLAY_REQUEST_TIME
   3792                                                READER_DISPLAY_SOLD_OUT);
   3793 
   3794   cmd_reader_display_internal_error = setup_mdb_cmd (
   3795     "Display Communication Error",
   3796     READER_DISPLAY_REQUEST,
   3797     READER_DISPLAY_REQUEST_TIME
   3798     READER_DISPLAY_INTERNAL_ERROR);
   3799 
   3800   cmd_reader_display_backend_not_reachable = setup_mdb_cmd (
   3801     "Display Backend not reachable",
   3802     READER_DISPLAY_REQUEST,
   3803     READER_DISPLAY_REQUEST_TIME
   3804     READER_DISPLAY_BACKEND_NOT_REACHABLE);
   3805   ret = GNUNET_PROGRAM_run (TALER_MDB_project_data (),
   3806                             argc,
   3807                             argv,
   3808                             "taler-mdb",
   3809                             "This is an application for snack machines to pay with GNU Taler via NFC.\n",
   3810                             options,
   3811                             &run,
   3812                             NULL);
   3813   if (! disable_tty)
   3814   {
   3815     if (have_tty)
   3816     {
   3817       /* Restore previous TTY settings */
   3818       if (0 != tcsetattr (STDIN_FILENO,
   3819                           TCSANOW,
   3820                           &tty_opts_backup))
   3821         fprintf (stderr,
   3822                  "Failed to restore terminal discipline\n");
   3823     }
   3824   }
   3825   if (GNUNET_NO == ret)
   3826     return 0;
   3827   if (GNUNET_OK != ret)
   3828     return 1;
   3829   return global_ret;
   3830 }