taler-mdb

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

taler-mdb.c (103417B)


      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_PostOrdersHandle *po;
    371 
    372   /**
    373    * Handle for a GET /private/orders/$ID operation.
    374    */
    375   struct TALER_MERCHANT_OrderMerchantGetHandle *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_OrderDeleteHandle *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_OrderRefundHandle *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_OS_Process *adv_child;
    653 
    654 /**
    655  * Handle to the process showing error messages
    656  * while we have one.
    657  */
    658 static struct GNUNET_OS_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_OS_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_OS_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_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ERR,
    883                                      NULL,
    884                                      NULL,
    885                                      NULL,
    886                                      argv[0],
    887                                      argv);
    888   if (NULL == ret)
    889     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    890                 "Failed to launch %s\n",
    891                 argv[0]);
    892   for (unsigned int i = 0; i<argc; i++)
    893     GNUNET_free (argv[i]);
    894   GNUNET_array_grow (argv,
    895                      argc,
    896                      0);
    897   GNUNET_free (cpy);
    898   return ret;
    899 }
    900 
    901 
    902 /**
    903  * Stop the advertising process.
    904  */
    905 static void
    906 stop_advertising (void)
    907 {
    908   if (NULL == adv_child)
    909     return;
    910   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    911               "Stopping advertising\n");
    912   GNUNET_break (0 ==
    913                 GNUNET_OS_process_kill (adv_child,
    914                                         SIGTERM));
    915   GNUNET_break (GNUNET_OK ==
    916                 GNUNET_OS_process_wait (adv_child));
    917   GNUNET_OS_process_destroy (adv_child);
    918   adv_child = NULL;
    919 }
    920 
    921 
    922 /**
    923  * Start the advertising process.
    924  */
    925 static void
    926 start_advertising (void)
    927 {
    928   if (NULL != err_child)
    929     return;
    930   stop_advertising (); /* just to be sure */
    931   if (NULL == adv_process_command)
    932     return;
    933   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    934               "Starting advertising\n");
    935   adv_child = start_command (adv_process_command,
    936                              NULL);
    937 }
    938 
    939 
    940 /**
    941  * Stop the process showing an error.
    942  */
    943 static void
    944 hide_error (void)
    945 {
    946   if (NULL == err_child)
    947     return;
    948   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    949               "Hiding error\n");
    950   if (NULL != err_stop_task)
    951   {
    952     GNUNET_SCHEDULER_cancel (err_stop_task);
    953     err_stop_task = NULL;
    954   }
    955   GNUNET_break (0 ==
    956                 GNUNET_OS_process_kill (err_child,
    957                                         SIGTERM));
    958   GNUNET_break (GNUNET_OK ==
    959                 GNUNET_OS_process_wait (err_child));
    960   GNUNET_OS_process_destroy (err_child);
    961   err_child = NULL;
    962 }
    963 
    964 
    965 /**
    966  * Show an error using the respective error process
    967  * command.
    968  *
    969  * @param err_type type of the error to pass to the command
    970  */
    971 static void
    972 show_error (const char *err_type)
    973 {
    974   stop_advertising ();
    975   hide_error (); /* just to be sure */
    976   if (NULL == err_process_command)
    977   {
    978     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    979                 "Cannot show error `%s'\n",
    980                 err_type);
    981     return;
    982   }
    983   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    984               "Showing error `%s' using `%s'\n",
    985               err_type,
    986               err_process_command);
    987   err_child = start_command (err_process_command,
    988                              err_type,
    989                              NULL);
    990   GNUNET_break (NULL != err_child);
    991 }
    992 
    993 
    994 /**
    995  * Task to stop the process showing an error.
    996  *
    997  * @param cls NULL
    998  */
    999 static void
   1000 do_hide_error (void *cls)
   1001 {
   1002   err_stop_task = NULL;
   1003   hide_error ();
   1004   start_advertising ();
   1005 }
   1006 
   1007 
   1008 /**
   1009  * Briefly show a temporary error.
   1010  *
   1011  * @param err_type error to show
   1012  */
   1013 static void
   1014 temporary_error (const char *err_type)
   1015 {
   1016   show_error (err_type);
   1017   if (NULL != err_stop_task)
   1018   {
   1019     GNUNET_SCHEDULER_cancel (err_stop_task);
   1020     err_stop_task = NULL;
   1021   }
   1022   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1023               "Will hide error in %s\n",
   1024               GNUNET_TIME_relative2s (ERR_DELAY,
   1025                                       true));
   1026   err_stop_task = GNUNET_SCHEDULER_add_delayed (ERR_DELAY,
   1027                                                 &do_hide_error,
   1028                                                 NULL);
   1029 }
   1030 
   1031 
   1032 #if HAVE_QRENCODE_H
   1033 #include <qrencode.h>
   1034 
   1035 /**
   1036  * @brief Create the QR code to pay and display it on screen
   1037  *
   1038  * @param uri what text to show in the QR code
   1039  */
   1040 static void
   1041 show_qrcode (const char *uri)
   1042 {
   1043   QRinput *qri;
   1044   QRcode *qrc;
   1045   unsigned int size;
   1046   char *upper;
   1047   char *base;
   1048   char *ubase;
   1049   size_t xOff;
   1050   size_t yOff;
   1051   const char *dddash;
   1052   unsigned int nwidth;
   1053 
   1054   stop_advertising ();
   1055   hide_error ();
   1056   if (0 > qrDisplay.devicefd)
   1057     return; /* no display, no dice */
   1058   /* find the fourth '/' in the payto://pay/hostname/-uri */
   1059   dddash = strchr (uri, '/');
   1060   if (NULL != dddash)
   1061     dddash = strchr (dddash + 1, '/');
   1062   if (NULL != dddash)
   1063     dddash = strchr (dddash + 1, '/');
   1064   if (NULL != dddash)
   1065     dddash = strchr (dddash + 1, '/');
   1066   if (NULL == dddash)
   1067   {
   1068     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1069                 "taler://pay/-URI malformed: `%s'\n",
   1070                 uri);
   1071     return;
   1072   }
   1073 
   1074   qri = QRinput_new2 (0, QR_ECLEVEL_L);
   1075   if (NULL == qri)
   1076   {
   1077     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   1078                          "QRinput_new2");
   1079     return;
   1080   }
   1081   /* convert all characters until the fourth '/' to upper
   1082      case. The rest _should_ be upper case in a NICE setup,
   1083      but we can't warrant it and must not touch those. */
   1084   base = GNUNET_strndup (uri,
   1085                          dddash - uri);
   1086 
   1087   ubase = GNUNET_STRINGS_utf8_toupper (base);
   1088   GNUNET_free (base);
   1089   GNUNET_asprintf (&upper,
   1090                    "%s%s",
   1091                    ubase,
   1092                    dddash);
   1093   GNUNET_free (ubase);
   1094   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1095               "Showing QR code for `%s'\n",
   1096               upper);
   1097   /* first try encoding as uppercase-only alpha-numerical
   1098      QR code (much smaller encoding); if that fails, also
   1099      try using binary encoding (in case nick contains
   1100      special characters). */
   1101   if ( (0 !=
   1102         QRinput_append (qri,
   1103                         QR_MODE_AN,
   1104                         strlen (upper),
   1105                         (unsigned char *) upper)) &&
   1106        (0 !=
   1107         QRinput_append (qri,
   1108                         QR_MODE_8,
   1109                         strlen (upper),
   1110                         (unsigned char *) upper)))
   1111   {
   1112     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   1113                          "QRinput_append");
   1114     GNUNET_free (upper);
   1115     return;
   1116   }
   1117   GNUNET_free (upper);
   1118   qrc = QRcode_encodeInput (qri);
   1119   if (NULL == qrc)
   1120   {
   1121     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   1122                          "QRcode_encodeInput");
   1123     QRinput_free (qri);
   1124     return;
   1125   }
   1126 
   1127   /* set QR-code border */
   1128   memset (qrDisplay.memory,
   1129           0xFF,
   1130           qrDisplay.var_info.xres * qrDisplay.var_info.yres
   1131           * sizeof (uint16_t));
   1132   size = GNUNET_MIN (qrDisplay.var_info.xres,
   1133                      qrDisplay.var_info.yres);
   1134 
   1135   nwidth = qrc->width + 8; /* +8 for 4 pixel border */
   1136   xOff = 4 * size / nwidth;
   1137   yOff = 4 * size / nwidth;
   1138 
   1139   /* calculate offset to show the code centered */
   1140   if (qrDisplay.var_info.xres < qrDisplay.var_info.yres)
   1141     yOff += (qrDisplay.var_info.yres - qrDisplay.var_info.xres) / 2;
   1142   else
   1143     xOff += (qrDisplay.var_info.xres - qrDisplay.var_info.yres) / 2;
   1144   for (unsigned int x = 0; x < qrDisplay.var_info.xres - 2 * xOff; x++)
   1145     for (unsigned int y = 0; y < qrDisplay.var_info.yres - 2 * yOff; y++)
   1146     {
   1147       unsigned int xoff = x * nwidth / size;
   1148       unsigned int yoff = y * nwidth / size;
   1149       unsigned int off = xoff + yoff * qrc->width;
   1150       if ( (xoff >= (unsigned) qrc->width) ||
   1151            (yoff >= (unsigned) qrc->width) )
   1152         continue;
   1153       /* set the pixels in the display memory */
   1154       qrDisplay.memory[(y + yOff) * qrDisplay.var_info.xres + (x + xOff)] =
   1155         (0 == (qrc->data[off] & 1)) ? 0xFFFF : 0x0000;
   1156     }
   1157 
   1158   QRcode_free (qrc);
   1159   QRinput_free (qri);
   1160 
   1161   /* Turn on backlight if supported */
   1162   if (0 < qrDisplay.backlightfd)
   1163     (void) ! write (qrDisplay.backlightfd,
   1164                     &backlight_on,
   1165                     1);
   1166 }
   1167 
   1168 
   1169 #endif
   1170 
   1171 
   1172 static void
   1173 run_mdb_event_loop (void);
   1174 
   1175 
   1176 /**
   1177  * Runs asynchronous cleanup part for freeing a
   1178  * payment activity.
   1179  *
   1180  * @param[in] cls a `struct PaymentActivity` to clean up
   1181  */
   1182 static void
   1183 async_pa_cleanup_job (void *cls)
   1184 {
   1185   struct PaymentActivity *pa = cls;
   1186 
   1187   if (NULL != pa->ctx)
   1188     GNUNET_CURL_fini (pa->ctx);
   1189   if (NULL != pa->rc)
   1190     GNUNET_CURL_gnunet_rc_destroy (pa->rc);
   1191   GNUNET_free (pa);
   1192   start_advertising ();
   1193 }
   1194 
   1195 
   1196 /**
   1197  * @brief Cleanup all the data when a order has succeeded or got cancelled
   1198  *
   1199  * @param pa the payment activity to clean up
   1200  */
   1201 static void
   1202 cleanup_payment (struct PaymentActivity *pa);
   1203 
   1204 
   1205 /**
   1206  * Function called with the result of the DELETE /orders/$ID operation.
   1207  *
   1208  * @param cls closure with the `struct PaymentActivity *`
   1209  * @param hr HTTP response details
   1210  */
   1211 static void
   1212 order_delete_cb (
   1213   void *cls,
   1214   const struct TALER_MERCHANT_HttpResponse *hr)
   1215 {
   1216   struct PaymentActivity *pa = cls;
   1217 
   1218   pa->odh = NULL;
   1219   if ( (MHD_HTTP_OK != hr->http_status) &&
   1220        (MHD_HTTP_NO_CONTENT != hr->http_status) )
   1221   {
   1222     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1223                 "Failed to delete incomplete order from backend: %d/%u\n",
   1224                 (int) hr->http_status,
   1225                 (unsigned int) hr->ec);
   1226   }
   1227   cleanup_payment (pa);
   1228 }
   1229 
   1230 
   1231 /**
   1232  * Clear the screen showing the QR code for the order.
   1233  *
   1234  * @param[in,out] pa payment activity to clear screen for
   1235  */
   1236 static void
   1237 clear_screen (struct PaymentActivity *pa)
   1238 {
   1239   if (NULL == pa->taler_pay_uri)
   1240     return;
   1241 #if HAVE_QRENCODE_H
   1242   if (NULL != qrDisplay.memory)
   1243     memset (qrDisplay.memory,
   1244             0xFF,
   1245             qrDisplay.var_info.xres * qrDisplay.var_info.yres
   1246             * sizeof (uint16_t));
   1247   if (0 < qrDisplay.backlightfd)
   1248     (void) ! write (qrDisplay.backlightfd,
   1249                     &backlight_off,
   1250                     1);
   1251 #endif
   1252   GNUNET_free (pa->taler_pay_uri);
   1253 }
   1254 
   1255 
   1256 static void
   1257 cleanup_payment (struct PaymentActivity *pa)
   1258 {
   1259   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1260               "Cleaning up payment\n");
   1261   if ( (! pa->paid) &&
   1262        (NULL != pa->order_id) )
   1263   {
   1264     char *oid;
   1265 
   1266     oid = pa->order_id;
   1267     pa->order_id = NULL;
   1268     pa->odh = TALER_MERCHANT_order_delete (
   1269       pa->ctx,
   1270       pa->base_url,
   1271       oid,
   1272       true, /* delete even claimed orders */
   1273       &order_delete_cb,
   1274       pa);
   1275     GNUNET_free (oid);
   1276     return;
   1277   }
   1278   if (NULL != pa->odh)
   1279   {
   1280     TALER_MERCHANT_order_delete_cancel (pa->odh);
   1281     pa->odh = NULL;
   1282   }
   1283   if (NULL != pa->pnd)
   1284   {
   1285     nfc_abort_command (pa->pnd);
   1286     nfc_close (pa->pnd);
   1287     pa->pnd = NULL;
   1288   }
   1289   if (NULL != cancelbutton_task)
   1290   {
   1291     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1292                 "Stopping watching of the cancel button\n");
   1293     GNUNET_SCHEDULER_cancel (cancelbutton_task);
   1294     cancelbutton_task = NULL;
   1295   }
   1296   if (NULL != pa->po)
   1297   {
   1298     TALER_MERCHANT_orders_post_cancel (pa->po);
   1299     pa->po = NULL;
   1300   }
   1301   if (NULL != pa->ogh)
   1302   {
   1303     TALER_MERCHANT_merchant_order_get_cancel (pa->ogh);
   1304     pa->ogh = NULL;
   1305   }
   1306   GNUNET_CURL_gnunet_scheduler_reschedule (&pa->rc);
   1307   if (NULL != pa->task)
   1308   {
   1309     GNUNET_SCHEDULER_cancel (pa->task);
   1310     pa->task = NULL;
   1311   }
   1312   if (NULL != pa->delay_task)
   1313   {
   1314     GNUNET_SCHEDULER_cancel (pa->delay_task);
   1315     pa->delay_task = NULL;
   1316   }
   1317   if (NULL != pa->delay_pay_task)
   1318   {
   1319     GNUNET_SCHEDULER_cancel (pa->delay_pay_task);
   1320     pa->delay_pay_task = NULL;
   1321   }
   1322   clear_screen (pa);
   1323   GNUNET_free (pa->order_id);
   1324   GNUNET_SCHEDULER_add_now (&async_pa_cleanup_job,
   1325                             pa);
   1326 }
   1327 
   1328 
   1329 /**
   1330  * @brief Shutdown the mdb communication tasks
   1331  */
   1332 static void
   1333 mdb_shutdown (void)
   1334 {
   1335   if (NULL != mdb.rtask)
   1336   {
   1337     GNUNET_SCHEDULER_cancel (mdb.rtask);
   1338     mdb.rtask = NULL;
   1339   }
   1340   if (NULL != mdb.wtask)
   1341   {
   1342     GNUNET_SCHEDULER_cancel (mdb.wtask);
   1343     mdb.wtask = NULL;
   1344   }
   1345   hide_error ();
   1346   stop_advertising ();
   1347   if (disable_mdb)
   1348     return;
   1349   /* restore UART */
   1350   if (0 != tcsetattr (mdb.uartfd,
   1351                       TCSAFLUSH,
   1352                       &mdb.uart_opts_backup))
   1353   {
   1354     printf ("Failed to restore uart discipline\n");
   1355     global_ret = EXIT_FAILURE;
   1356   }
   1357   if (-1 != mdb.uartfd)
   1358   {
   1359     GNUNET_break (0 == close (mdb.uartfd));
   1360     mdb.uartfd = -1;
   1361   }
   1362   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1363               "Shutdown complete (including MDB)\n");
   1364 }
   1365 
   1366 
   1367 /**
   1368  * @brief Shutdown the application.
   1369  *
   1370  * @param cls closure
   1371  */
   1372 static void
   1373 shutdown_task (void *cls)
   1374 {
   1375   struct Refund *r;
   1376 
   1377   (void) cls;
   1378   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1379               "Shutdown initiated\n");
   1380   stop_advertising ();
   1381   while (NULL != (r = refund_head))
   1382   {
   1383     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1384                 "Pending refund operation aborted due to shutdown\n");
   1385     GNUNET_CONTAINER_DLL_remove (refund_head,
   1386                                  refund_tail,
   1387                                  r);
   1388     TALER_MERCHANT_post_order_refund_cancel (r->orh);
   1389     GNUNET_free (r);
   1390   }
   1391   if (NULL != context)
   1392   {
   1393     nfc_exit (context);
   1394     context = NULL;
   1395   }
   1396   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1397               "NFC down\n");
   1398   if (NULL != payment_activity)
   1399   {
   1400     cleanup_payment (payment_activity);
   1401     payment_activity = NULL;
   1402   }
   1403   if (NULL != cancelbutton_task)
   1404   {
   1405     GNUNET_SCHEDULER_cancel (cancelbutton_task);
   1406     cancelbutton_task = NULL;
   1407   }
   1408   if (NULL != keyboard_task)
   1409   {
   1410     GNUNET_SCHEDULER_cancel (keyboard_task);
   1411     keyboard_task = NULL;
   1412   }
   1413   /* last ditch saying nicely goodbye to MDB */
   1414   in_shutdown = GNUNET_YES;
   1415   mdb.cmd = &endSession;
   1416   if (-1 != mdb.uartfd)
   1417     run_mdb_event_loop ();
   1418   if ( (MAP_FAILED != qrDisplay.memory) &&
   1419        (NULL != qrDisplay.memory) )
   1420   {
   1421     /* free the display data  */
   1422     munmap (qrDisplay.memory,
   1423             qrDisplay.fix_info.smem_len);
   1424     qrDisplay.memory = NULL;
   1425     /* reset original state */
   1426     if (0 > ioctl (qrDisplay.devicefd,
   1427                    FBIOPUT_VSCREENINFO,
   1428                    &qrDisplay.orig_vinfo))
   1429     {
   1430       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1431                   "failed to reset originial display state\n");
   1432     }
   1433     /* close the device */
   1434     GNUNET_break (0 == close (qrDisplay.devicefd));
   1435     qrDisplay.devicefd = -1;
   1436     if (0 < qrDisplay.backlightfd)
   1437       GNUNET_break (0 == close (qrDisplay.backlightfd));
   1438     qrDisplay.backlightfd = -1;
   1439   }
   1440   if (-1 != cancel_button.cancelbuttonfd)
   1441   {
   1442     GNUNET_break (0 ==
   1443                   close (cancel_button.cancelbuttonfd));
   1444     cancel_button.cancelbuttonfd = -1;
   1445   }
   1446   {
   1447     int efd;
   1448 
   1449     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1450                 "Unexporting GPIO pin 23\n");
   1451     efd = open ("/sys/class/gpio/unexport",
   1452                 O_WRONLY);
   1453     if (-1 == efd)
   1454     {
   1455       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1456                   "Unable to open /gpio/unexport for cancel button\n");
   1457     }
   1458     else
   1459     {
   1460       if (2 != write (efd,
   1461                       "23",
   1462                       2))
   1463       {
   1464         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   1465                                   "write",
   1466                                   "/sys/class/gpio/unexport");
   1467 
   1468       }
   1469       GNUNET_break (0 == close (efd));
   1470     }
   1471   }
   1472   /* free the allocated productes read from config file */
   1473   if (NULL != products)
   1474   {
   1475     for (unsigned int i = 0; i < products_length; i++)
   1476     {
   1477       GNUNET_free (products[i].description);
   1478       GNUNET_free (products[i].auth_header);
   1479       GNUNET_free (products[i].instance);
   1480       GNUNET_free (products[i].preview);
   1481     }
   1482     GNUNET_array_grow (products,
   1483                        products_length,
   1484                        0);
   1485   }
   1486   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1487               "Shutdown complete (except for MDB)\n");
   1488 }
   1489 
   1490 
   1491 static void
   1492 check_payment_again (void *cls);
   1493 
   1494 
   1495 static void
   1496 connect_target (void *cls);
   1497 
   1498 
   1499 static void
   1500 wallet_select_aid (void *cls);
   1501 
   1502 
   1503 /**
   1504  * @brief Transmit the pay uri from taler to the wallet application via NFC
   1505  *
   1506  * @param cls closure
   1507  */
   1508 static void
   1509 wallet_transmit_uri (void *cls)
   1510 {
   1511   struct PaymentActivity *pa = cls;
   1512   /* response array for APDU response status word */
   1513   uint8_t response[] = { 0x00, 0x00 };
   1514   size_t slen = strlen (pa->taler_pay_uri);
   1515   uint8_t message[sizeof (put_data) + slen];
   1516 
   1517   pa->delay_task = NULL;
   1518   /* append the pay uri to the put data command */
   1519   memcpy (message, put_data, sizeof (put_data));
   1520   memcpy (&message[sizeof (put_data)], pa->taler_pay_uri, slen);
   1521   /* send the put data command via nfc */
   1522   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1523               "Sending 'PUT DATA' command for `%s' to wallet\n",
   1524               pa->taler_pay_uri);
   1525   if (0 > nfc_initiator_transceive_bytes (pa->pnd,
   1526                                           message,
   1527                                           sizeof (message),
   1528                                           response,
   1529                                           sizeof(response),
   1530                                           NFC_TIMEOUT))
   1531   {
   1532     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1533                 "Failed to send command via NFC\n");
   1534     pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1535                                          pa);
   1536     return;
   1537   }
   1538   /* check if the transmission succeeded */
   1539   if (0 != memcmp (response,
   1540                    APDU_SUCCESS,
   1541                    sizeof (response)))
   1542   {
   1543     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1544                 "'PUT DATA' command transmission failed, return code: %x%x\n",
   1545                 response[0],
   1546                 response[1]);
   1547     pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1548                                          pa);
   1549     return;
   1550   }
   1551   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1552               "'PUT DATA' command sent successfully via NFC\n");
   1553   pa->wallet_has_uri = true;
   1554   /* FIXME: or just offer Internet service here? */
   1555 
   1556   /* transmit the uri again later, there can be many external failures,
   1557      for example the taler wallet app was not opened and thus did not receive
   1558      the data */
   1559   pa->delay_task = GNUNET_SCHEDULER_add_delayed (MAX_HTTP_RETRY_FREQ,
   1560                                                  &wallet_transmit_uri,
   1561                                                  pa);
   1562 }
   1563 
   1564 
   1565 /**
   1566  * @brief Select the taler wallet app via NFC on the target selected with
   1567  * @e connect_target()
   1568  * (check if it is installed on the smartphone)
   1569  *
   1570  * @param cls closure
   1571  */
   1572 static void
   1573 wallet_select_aid (void *cls)
   1574 {
   1575   struct PaymentActivity *pa = cls;
   1576   /* response array for APDU response status word */
   1577   uint8_t response[] = { 0x00, 0x00 };
   1578   uint8_t message[sizeof(select_file) + sizeof(taler_aid)];
   1579 
   1580   pa->task = NULL;
   1581   /* append the taler wallet aid to the select file command */
   1582   memcpy (message,
   1583           select_file,
   1584           sizeof (select_file));
   1585   memcpy (&message[sizeof (select_file)],
   1586           taler_aid,
   1587           sizeof (taler_aid));
   1588 
   1589   /* send the select file command via nfc */
   1590   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1591               "Trying to find Taler wallet on NFC\n");
   1592   if (0 > nfc_initiator_transceive_bytes (pa->pnd,
   1593                                           message,
   1594                                           sizeof (message),
   1595                                           response,
   1596                                           sizeof (response),
   1597                                           NFC_TIMEOUT))
   1598   {
   1599     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1600                 "Failed to transceive with NFC app, trying to find another NFC client\n");
   1601     pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1602                                          pa);
   1603     return;
   1604   }
   1605   /* check if the transmission succeeded */
   1606   if (0 == memcmp (response,
   1607                    APDU_SUCCESS,
   1608                    sizeof (response)))
   1609   {
   1610     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1611                 "Taler wallet found over NFC\n");
   1612     pa->delay_task = GNUNET_SCHEDULER_add_now (&wallet_transmit_uri,
   1613                                                pa);
   1614     return;
   1615   }
   1616   /* if the transmission was not successful chack if the app is available at all */
   1617   if (0 == memcmp (response,
   1618                    APDU_NOT_FOUND,
   1619                    sizeof (response)))
   1620   {
   1621     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1622                 "Taler wallet NOT found on this device\n");
   1623     pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1624                                          pa);
   1625     return;
   1626   }
   1627   /* If the upper cases did not match, there was an unknown APDU status returned */
   1628   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1629               "AID selection failure, return code: %x%x, trying to find another NFC client\n",
   1630               response[0],
   1631               response[1]);
   1632   /* start the selection again */
   1633   pa->task = GNUNET_SCHEDULER_add_delayed (NFC_NOT_FOUND_RETRY_FREQ,
   1634                                            &connect_target,
   1635                                            pa);
   1636 }
   1637 
   1638 
   1639 /**
   1640  * @brief Connect the NFC reader with a compatible NFC target
   1641  *
   1642  * @param cls closure
   1643  */
   1644 static void
   1645 connect_target (void *cls)
   1646 {
   1647   struct PaymentActivity *pa = cls;
   1648   /* nfc modulation used */
   1649   const nfc_modulation nmMifare = {
   1650     .nmt = NMT_ISO14443A,
   1651     .nbr = NBR_212,
   1652   };
   1653 
   1654   pa->task = NULL;
   1655   /* set the uid len to zero (maybe it is still set from earlier selections) */
   1656   pa->nt.nti.nai.szUidLen = 0;
   1657   /* poll for a fitting nfc target (we use the shortest time possible to not block the scheduler) */
   1658   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1659               "Trying to find NFC client\n");
   1660   if (0 > nfc_initiator_poll_target (pa->pnd,
   1661                                      &nmMifare,
   1662                                      1,
   1663                                      0x01,
   1664                                      0x01,
   1665                                      &pa->nt))
   1666   {
   1667     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1668                 "Failed to connect to NFC target\n");
   1669   }
   1670   /* if the uid length are out of bound abort */
   1671   else if ( (pa->nt.nti.nai.szUidLen > UID_LEN_UPPER) ||
   1672             (pa->nt.nti.nai.szUidLen < UID_LEN_LOWER) )
   1673   {
   1674     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1675                 "Failed to connect, wrong NFC modulation\n");
   1676   }
   1677   else
   1678   {
   1679     /* the target was successfully selected,
   1680        now we have to check if the taler wallet is installed on it */
   1681     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1682                 "Found NFC client\n");
   1683     pa->task = GNUNET_SCHEDULER_add_now (&wallet_select_aid,
   1684                                          pa);
   1685     return;
   1686   }
   1687   /* if no target was found try again */
   1688   pa->task = GNUNET_SCHEDULER_add_delayed (NFC_NOT_FOUND_RETRY_FREQ,
   1689                                            &connect_target,
   1690                                            pa);
   1691 }
   1692 
   1693 
   1694 /**
   1695  * @brief Open the NFC reader.
   1696  *
   1697  * @param cls closure
   1698  */
   1699 static void
   1700 open_nfc_reader (void *cls)
   1701 {
   1702   struct PaymentActivity *pa = cls;
   1703 
   1704   pa->task = NULL;
   1705   /* open the nfc reader via libnfc's open */
   1706   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1707               "Trying to open NFC device\n");
   1708   pa->pnd = nfc_open (context, NULL);
   1709   if (NULL == pa->pnd)
   1710   {
   1711     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1712                 "Payment inititation: Unable to open NFC device\n");
   1713     pa->task = GNUNET_SCHEDULER_add_delayed (NFC_FAILURE_RETRY_FREQ,
   1714                                              &open_nfc_reader,
   1715                                              pa);
   1716     return;
   1717   }
   1718   /* initialize the reader as initiator */
   1719   if (0 > nfc_initiator_init (pa->pnd))
   1720   {
   1721     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1722                 "Failed to initialize NFC device: %s\n",
   1723                 nfc_strerror (pa->pnd));
   1724     cleanup_payment (pa);
   1725     GNUNET_assert (payment_activity == pa);
   1726     payment_activity = NULL;
   1727     return;
   1728   }
   1729   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1730               "NFC in operation %s / %s\n",
   1731               nfc_device_get_name (pa->pnd),
   1732               nfc_device_get_connstring (pa->pnd));
   1733   /* the nfc reader was opened successfully, now try to find a mobile device as a target */
   1734   pa->task = GNUNET_SCHEDULER_add_now (&connect_target,
   1735                                        pa);
   1736 }
   1737 
   1738 
   1739 static void
   1740 start_read_keyboard (void);
   1741 
   1742 
   1743 /**
   1744  * @brief Callback to process a GET /check-payment request
   1745  *
   1746  * @param cls closure
   1747  * @param osr order status response details (on success)
   1748  */
   1749 static void
   1750 check_payment_cb (void *cls,
   1751                   const struct TALER_MERCHANT_OrderStatusResponse *osr)
   1752 {
   1753   struct PaymentActivity *pa = cls;
   1754   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
   1755 
   1756   GNUNET_assert (payment_activity == pa);
   1757   pa->ogh = NULL;
   1758   if ( (MHD_HTTP_OK != hr->http_status) &&
   1759        (MHD_HTTP_GATEWAY_TIMEOUT != hr->http_status) &&
   1760        (MHD_HTTP_REQUEST_TIMEOUT != hr->http_status) )
   1761   {
   1762     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1763                 "Backend request to GET /orders/$ID failed: %u/%d\n",
   1764                 hr->http_status,
   1765                 (int) hr->ec);
   1766     mdb.cmd = &cmd_reader_display_backend_not_reachable;
   1767     temporary_error ("backend-unexpected-failure");
   1768     run_mdb_event_loop ();
   1769     cleanup_payment (pa);
   1770     GNUNET_assert (payment_activity == pa);
   1771     payment_activity = NULL;
   1772     return;
   1773   }
   1774 
   1775   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1776               "Backend request to GET /orders/$ID returned: %u\n",
   1777               hr->http_status);
   1778   if ( (MHD_HTTP_OK == hr->http_status) &&
   1779        (TALER_MERCHANT_OSC_PAID == osr->details.ok.status) )
   1780   {
   1781     clear_screen (pa);
   1782     clear_error_on_start = true;
   1783     temporary_error ("dispensing");
   1784     mdb.cmd = &cmd_approve_vend;
   1785     payment_activity->paid = true;
   1786     run_mdb_event_loop ();
   1787     if ((disable_mdb) && (! disable_tty))
   1788     {
   1789       GNUNET_SCHEDULER_cancel (keyboard_task);
   1790       keyboard_task = NULL;
   1791       start_read_keyboard ();
   1792     }
   1793     return;
   1794   }
   1795   /* Start to check for payment. Note that we do this even before
   1796      we talked successfully to the wallet via NFC because we MAY show the
   1797      QR code in the future and in that case the payment may happen
   1798      anytime even before the NFC communication succeeds. */
   1799   if ( (NULL == pa->ogh) &&
   1800        (NULL == pa->delay_pay_task) )
   1801   {
   1802     pa->delay_pay_task = GNUNET_SCHEDULER_add_delayed (MAX_HTTP_RETRY_FREQ,
   1803                                                        &check_payment_again,
   1804                                                        pa);
   1805   }
   1806   if ( (NULL == pa->taler_pay_uri) &&
   1807        (MHD_HTTP_OK == hr->http_status) &&
   1808        (TALER_MERCHANT_OSC_UNPAID == osr->details.ok.status) )
   1809   {
   1810     char *uri;
   1811 
   1812     uri = GNUNET_strdup (osr->details.ok.details.unpaid.taler_pay_uri);
   1813     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1814                 "Trying to talk to wallet to give it pay URI `%s'\n",
   1815                 uri);
   1816     GNUNET_assert (NULL == pa->pnd);
   1817     pa->taler_pay_uri = uri;
   1818 #if HAVE_QRENCODE_H
   1819     show_qrcode (uri);
   1820 #endif
   1821     pa->task = GNUNET_SCHEDULER_add_now (&open_nfc_reader,
   1822                                          pa);
   1823   }
   1824 }
   1825 
   1826 
   1827 /**
   1828  * @brief Check the payment status again
   1829  *
   1830  * @param cls closure
   1831  */
   1832 static void
   1833 check_payment_again (void *cls)
   1834 {
   1835   struct PaymentActivity *pa = cls;
   1836 
   1837   pa->delay_pay_task = NULL;
   1838   GNUNET_assert (NULL == pa->ogh);
   1839   pa->ogh = TALER_MERCHANT_merchant_order_get (pa->ctx,
   1840                                                pa->base_url,
   1841                                                pa->order_id,
   1842                                                NULL /* snack machine, no Web crap */
   1843                                                ,
   1844                                                BACKEND_POLL_TIMEOUT,
   1845                                                &check_payment_cb,
   1846                                                pa);
   1847 }
   1848 
   1849 
   1850 /**
   1851  * @brief Callback for a POST /orders request
   1852  *
   1853  * @param cls closure
   1854  * @param por response for this request
   1855  */
   1856 static void
   1857 proposal_cb (void *cls,
   1858              const struct TALER_MERCHANT_PostOrdersReply *por)
   1859 {
   1860   struct PaymentActivity *pa = cls;
   1861 
   1862   pa->po = NULL;
   1863   if (MHD_HTTP_OK != por->hr.http_status)
   1864   {
   1865     /* FIXME: In the future, we may want to support MHD_HTTP_GONE
   1866        explicitly and show 'product out of stock' here! */
   1867     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1868                 "Failed to setup order with backend: %u/%d\n",
   1869                 por->hr.http_status,
   1870                 (int) por->hr.ec);
   1871     json_dumpf (por->hr.reply,
   1872                 stderr,
   1873                 0);
   1874     temporary_error ("backend-temporary-failure");
   1875     mdb.cmd = &cmd_reader_display_backend_not_reachable;
   1876     run_mdb_event_loop ();
   1877     cleanup_payment (pa);
   1878     GNUNET_assert (payment_activity == pa);
   1879     payment_activity = NULL;
   1880     return;
   1881   }
   1882   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1883               "Backend successfully created order `%s'\n",
   1884               por->details.ok.order_id);
   1885   pa->order_id = GNUNET_strdup (por->details.ok.order_id);
   1886   pa->ogh = TALER_MERCHANT_merchant_order_get (pa->ctx,
   1887                                                pa->base_url,
   1888                                                pa->order_id,
   1889                                                NULL /* snack machine, no Web crap */
   1890                                                ,
   1891                                                GNUNET_TIME_UNIT_ZERO,
   1892                                                &check_payment_cb,
   1893                                                pa);
   1894 }
   1895 
   1896 
   1897 static void
   1898 start_read_cancel_button (void);
   1899 
   1900 
   1901 /**
   1902  * @brief Launch a new order
   1903  *
   1904  * @param product information for product to sell
   1905  * @return payment activity for the order, NULL on failure
   1906  */
   1907 static struct PaymentActivity *
   1908 launch_payment (struct Product *product)
   1909 {
   1910   static const char *uuids[1];
   1911   struct PaymentActivity *pa;
   1912   json_t *orderReq;
   1913   char *msg;
   1914   const char *pos;
   1915 
   1916   pos = strstr (fulfillment_msg,
   1917                 "${PRODUCT_DESCRIPTION}");
   1918   if (NULL != pos)
   1919   {
   1920     /* replace ${PRODUCT_DESCRIPTION} with the real one */
   1921     GNUNET_asprintf (&msg,
   1922                      "%.*s%s%s",
   1923                      /* first output URL until ${PRODUCT_DESCRIPTION} */
   1924                      (int) (pos - fulfillment_msg),
   1925                      fulfillment_msg,
   1926                      /* replace ${PRODUCT_DESCRIPTION} with the right description */
   1927                      product->description,
   1928                      /* append rest of original URL */
   1929                      pos + strlen ("${PRODUCT_DESCRIPTION}"));
   1930   }
   1931   else
   1932   {
   1933     msg = GNUNET_strdup (fulfillment_msg);
   1934   }
   1935 
   1936   /* create the json object for the order request */
   1937   if (NULL != product->preview)
   1938   {
   1939     json_t *lproducts;
   1940 
   1941     lproducts = json_array ();
   1942     GNUNET_assert (NULL != lproducts);
   1943     GNUNET_assert (
   1944       0 ==
   1945       json_array_append_new (lproducts,
   1946                              GNUNET_JSON_PACK (
   1947                                GNUNET_JSON_pack_string ("description",
   1948                                                         product->description),
   1949                                GNUNET_JSON_pack_string ("image",
   1950                                                         product->preview),
   1951                                TALER_JSON_pack_amount ("price",
   1952                                                        &product->price),
   1953                                GNUNET_JSON_pack_uint64 ("quantity",
   1954                                                         1))));
   1955     orderReq = GNUNET_JSON_PACK (
   1956       GNUNET_JSON_pack_string ("summary",
   1957                                product->description),
   1958 #if BUG
   1959       GNUNET_JSON_pack_timestamp ("pay_deadline",
   1960                                   GNUNET_TIME_relative_to_timestamp (
   1961                                     PAY_TIMEOUT)),
   1962 #endif
   1963       GNUNET_JSON_pack_array_steal (
   1964         "products",
   1965         lproducts),
   1966       TALER_JSON_pack_amount ("amount",
   1967                               &product->price),
   1968       GNUNET_JSON_pack_string ("fulfillment_message",
   1969                                msg),
   1970       GNUNET_JSON_pack_time_rel ("auto_refund",
   1971                                  MAX_REFUND_DELAY));
   1972   }
   1973   else
   1974   {
   1975     orderReq = GNUNET_JSON_PACK (
   1976       GNUNET_JSON_pack_string ("summary",
   1977                                product->description),
   1978 #if BUG
   1979       GNUNET_JSON_pack_timestamp ("pay_deadline",
   1980                                   GNUNET_TIME_relative_to_timestamp (
   1981                                     PAY_TIMEOUT)),
   1982 #endif
   1983       TALER_JSON_pack_amount ("amount",
   1984                               &product->price),
   1985       GNUNET_JSON_pack_string ("fulfillment_message",
   1986                                msg),
   1987       GNUNET_JSON_pack_time_rel ("auto_refund",
   1988                                  MAX_REFUND_DELAY));
   1989   }
   1990   GNUNET_free (msg);
   1991   if (NULL == orderReq)
   1992   {
   1993     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1994                 "json_pack failed (out of memory?)\n");
   1995     return NULL;
   1996   }
   1997   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1998               "Creating order for `%s' at backend `%s'\n",
   1999               product->description,
   2000               (NULL == product->instance)
   2001               ? backend_base_url
   2002               : product->instance);
   2003   pa = GNUNET_new (struct PaymentActivity);
   2004   pa->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   2005                               &pa->rc);
   2006   pa->rc = GNUNET_CURL_gnunet_rc_create (pa->ctx);
   2007   GNUNET_assert (GNUNET_OK ==
   2008                  GNUNET_CURL_append_header (pa->ctx,
   2009                                             product->auth_header));
   2010   pa->product = product;
   2011   pa->amount = product->price;
   2012   /* put the order on the merchant's backend */
   2013   pa->base_url = (NULL == product->instance)
   2014                  ? backend_base_url
   2015                  : product->instance;
   2016   GNUNET_assert (NULL == pa->po);
   2017   pa->po = TALER_MERCHANT_orders_post2 (pa->ctx,
   2018                                         pa->base_url,
   2019                                         orderReq,
   2020                                         MAX_REFUND_DELAY,
   2021                                         NULL, /* no payment target preference */
   2022                                         0,
   2023                                         NULL, /* no inventory */
   2024                                         0,
   2025                                         uuids, /* no locks */
   2026                                         false, /* no claim token */
   2027                                         &proposal_cb,
   2028                                         pa);
   2029   json_decref (orderReq);
   2030   if (NULL == pa->po)
   2031   {
   2032     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2033                 "TALER_MERCHANT_order_put failed (out of memory?)\n");
   2034     temporary_error ("internal-failure");
   2035     cleanup_payment (pa);
   2036     return NULL;
   2037   }
   2038   /* Start to read the button on the VM to cancel this payment */
   2039   if (-1 != cancel_button.cancelbuttonfd)
   2040   {
   2041     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2042                 "Payment started, watching for cancel button\n");
   2043     start_read_cancel_button ();
   2044   }
   2045   return pa;
   2046 }
   2047 
   2048 
   2049 /**
   2050  * @brief Vending successful, conclude payment activity.
   2051  */
   2052 static void
   2053 vend_success (void)
   2054 {
   2055   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2056               "MDB vend success received\n");
   2057   GNUNET_break (NULL != payment_activity);
   2058   if (NULL != payment_activity)
   2059   {
   2060     cleanup_payment (payment_activity);
   2061     payment_activity = NULL;
   2062   }
   2063   if (clear_error_on_start)
   2064   {
   2065     hide_error ();
   2066     start_advertising ();
   2067   }
   2068 }
   2069 
   2070 
   2071 /**
   2072  * Runs asynchronous cleanup part for freeing a
   2073  * refund activity.
   2074  *
   2075  * @param[in] cls a `struct Refund` to clean up
   2076  */
   2077 static void
   2078 async_refund_cleanup_job (void *cls)
   2079 {
   2080   struct Refund *r = cls;
   2081 
   2082   if (NULL != r->ctx)
   2083     GNUNET_CURL_fini (r->ctx);
   2084   if (NULL != r->rc)
   2085     GNUNET_CURL_gnunet_rc_destroy (r->rc);
   2086   GNUNET_free (r);
   2087 }
   2088 
   2089 
   2090 /**
   2091  * @brief Callback to process a POST /refund request
   2092  *
   2093  * @param cls closure
   2094  * @param rr response details
   2095  */
   2096 static void
   2097 refund_complete_cb (void *cls,
   2098                     const struct TALER_MERCHANT_RefundResponse *rr)
   2099 {
   2100   struct Refund *r = cls;
   2101 
   2102   r->orh = NULL;
   2103   if (MHD_HTTP_OK != rr->hr.http_status)
   2104   {
   2105     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2106                 "Failed to grant consumer refund: %u/%d\n",
   2107                 rr->hr.http_status,
   2108                 (int) rr->hr.ec);
   2109   }
   2110   GNUNET_CONTAINER_DLL_remove (refund_head,
   2111                                refund_tail,
   2112                                r);
   2113   GNUNET_SCHEDULER_add_now (&async_refund_cleanup_job,
   2114                             r);
   2115 }
   2116 
   2117 
   2118 /**
   2119  * @brief Vending failed, provide refund.
   2120  */
   2121 static void
   2122 vend_failure (void)
   2123 {
   2124   struct Product *p;
   2125   struct Refund *r;
   2126 
   2127   if (NULL == payment_activity)
   2128   {
   2129     GNUNET_break (0);
   2130     return;
   2131   }
   2132   p = payment_activity->product;
   2133   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2134               "Received MDB vend failure for `%s', refunding customer\n",
   2135               p->description);
   2136   p->sold_out = true;
   2137   mdb.cmd = &endSession;
   2138   mdb.session_running = false;
   2139   r = GNUNET_new (struct Refund);
   2140   r->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   2141                              &r->rc);
   2142   r->rc = GNUNET_CURL_gnunet_rc_create (r->ctx);
   2143   GNUNET_assert (GNUNET_OK ==
   2144                  GNUNET_CURL_append_header (r->ctx,
   2145                                             p->auth_header));
   2146   r->orh = TALER_MERCHANT_post_order_refund (r->ctx,
   2147                                              (NULL == p->instance)
   2148                                              ? backend_base_url
   2149                                              : p->instance,
   2150                                              payment_activity->order_id,
   2151                                              &payment_activity->amount,
   2152                                              "failed to dispense product",
   2153                                              &refund_complete_cb,
   2154                                              r);
   2155   if (NULL == r->orh)
   2156   {
   2157     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2158                 "Failed to launch refund interaction with the merchant backend!\n");
   2159     GNUNET_free (r);
   2160   }
   2161   else
   2162   {
   2163     GNUNET_CONTAINER_DLL_insert (refund_head,
   2164                                  refund_tail,
   2165                                  r);
   2166   }
   2167   if (NULL != payment_activity)
   2168   {
   2169     cleanup_payment (payment_activity);
   2170     payment_activity = NULL;
   2171   }
   2172 }
   2173 
   2174 
   2175 /**
   2176  * @brief Read the character from stdin and activate the selected task
   2177  *
   2178  * @param cls closure
   2179  */
   2180 static void
   2181 read_keyboard_command (void *cls)
   2182 {
   2183   int input;
   2184 
   2185   (void) cls;
   2186   keyboard_task = NULL;
   2187   input = getchar ();
   2188   if ( (EOF == input) ||
   2189        ('x' == (char) input) )
   2190   {
   2191     GNUNET_SCHEDULER_shutdown ();
   2192     return;
   2193   }
   2194   if (NULL != payment_activity)
   2195   {
   2196     switch ((char) input)
   2197     {
   2198     case 'c':
   2199       if (GNUNET_NO == payment_activity->paid)
   2200       {
   2201         mdb.cmd = &cmd_deny_vend;
   2202       }
   2203       else
   2204       {
   2205         mdb.cmd = &endSession;
   2206         mdb.session_running = false;
   2207       }
   2208       run_mdb_event_loop ();
   2209       cleanup_payment (payment_activity);
   2210       payment_activity = NULL;
   2211       break;
   2212     case 'a':
   2213       payment_activity->paid = true;
   2214       mdb.cmd = &cmd_approve_vend;
   2215       run_mdb_event_loop ();
   2216       break;
   2217     case 'n':
   2218       if (disable_mdb)
   2219       {
   2220         vend_failure ();
   2221       }
   2222       else
   2223       {
   2224         fprintf (stderr,
   2225                  "Cannot fail to vend at this time, waiting for payment\n");
   2226       }
   2227       break;
   2228     case 'y':
   2229       if (disable_mdb)
   2230       {
   2231         vend_success ();
   2232       }
   2233       else
   2234       {
   2235         fprintf (stderr,
   2236                  "Cannot succeed to vend at this time, waiting for payment\n");
   2237       }
   2238       break;
   2239     default:
   2240       fprintf (stderr,
   2241                "Unknown command `%c'\n",
   2242                input);
   2243       break;
   2244     }
   2245     start_read_keyboard ();
   2246     return;
   2247   }
   2248   if (NULL != payment_activity)
   2249   {
   2250     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2251                 "Purchase activity already pending\n");
   2252     start_read_keyboard ();
   2253     return;
   2254   }
   2255   for (unsigned int i = 0; i < products_length; i++)
   2256     if (((char) input) == products[i].key)
   2257     {
   2258       if ( (sold_out_enabled) &&
   2259            (products[i].sold_out) )
   2260       {
   2261         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2262                     "Product %s sold out, denying vend\n",
   2263                     products[i].description);
   2264         mdb.cmd = &cmd_reader_display_sold_out;
   2265         run_mdb_event_loop ();
   2266         start_read_keyboard ();
   2267         return;
   2268       }
   2269       payment_activity = launch_payment (&products[i]);
   2270       start_read_keyboard ();
   2271       return;
   2272     }
   2273   fprintf (stderr,
   2274            "Unknown command '%c'\n",
   2275            (char) input);
   2276   start_read_keyboard ();
   2277 }
   2278 
   2279 
   2280 /**
   2281  * @brief Read the state of the cancel button GPIO pin
   2282  *
   2283  * @param cls closure
   2284  */
   2285 static void
   2286 cancel_button_pressed (void *cls)
   2287 {
   2288   char value;
   2289 
   2290   (void) cls;
   2291   cancelbutton_task = NULL;
   2292   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2293               "Cancel button event detected\n");
   2294   if (1 !=
   2295       read (cancel_button.cancelbuttonfd,
   2296             &value,
   2297             1))
   2298   {
   2299     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
   2300                          "read");
   2301     start_read_cancel_button ();
   2302     return;
   2303   }
   2304 
   2305   GNUNET_break (0 == lseek (cancel_button.cancelbuttonfd,
   2306                             0,
   2307                             SEEK_SET));
   2308   /* This point should only be reached  when a order is pending, because
   2309    * the scheduler read file gets added in the function "launch_payment".
   2310    * But anyway safe check, else do nothing */
   2311   if (NULL != payment_activity)
   2312   {
   2313     if ('1' == value)
   2314     {
   2315       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2316                   "Cancel button pressed, canceling current order\n");
   2317       if (GNUNET_NO == payment_activity->paid)
   2318       {
   2319         /* The current payment was not paid already, deny it */
   2320         mdb.cmd = &cmd_deny_vend;
   2321       }
   2322       else
   2323       {
   2324         /* The order was paid and if we know this, then it is also yielded,
   2325          * just end the current session */
   2326         mdb.cmd = &endSession;
   2327         mdb.session_running = false;
   2328       }
   2329       run_mdb_event_loop ();
   2330       cleanup_payment (payment_activity);
   2331       payment_activity = NULL;
   2332     }
   2333     else
   2334     {
   2335       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2336                   "Cancel button spurious event (%d), looking for more\n",
   2337                   (int) value);
   2338       start_read_cancel_button ();
   2339     }
   2340   }
   2341 }
   2342 
   2343 
   2344 /**
   2345  * @brief Wait for a keyboard input
   2346  */
   2347 static void
   2348 start_read_keyboard (void)
   2349 {
   2350   static struct GNUNET_DISK_FileHandle fh = { STDIN_FILENO };
   2351 
   2352   GNUNET_assert (NULL == keyboard_task);
   2353   if (NULL == payment_activity)
   2354   {
   2355     for (unsigned int i = 0; i < products_length; i++)
   2356       printf ("'%c' to buy %s\n",
   2357               products[i].key,
   2358               products[i].description);
   2359   }
   2360   else
   2361   {
   2362     if (GNUNET_NO == payment_activity->paid)
   2363     {
   2364       printf ("'a' to fake payment for the last purchase\n"
   2365               "'c' to cancel last purchase\n");
   2366     }
   2367     else
   2368     {
   2369       if (disable_mdb)
   2370         printf ("'y' to simulate product successfully dispensed\n"
   2371                 "'n' to simulate product failed to be dispensed\n");
   2372     }
   2373   }
   2374   printf ("'x' to quit\n");
   2375   printf ("Waiting for keyboard input\n");
   2376   keyboard_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
   2377                                                   &fh,
   2378                                                   &read_keyboard_command,
   2379                                                   NULL);
   2380 }
   2381 
   2382 
   2383 /**
   2384  * @brief Wait for cancel button during payment activity
   2385  */
   2386 static void
   2387 start_read_cancel_button (void)
   2388 {
   2389   struct GNUNET_DISK_FileHandle fh = {
   2390     cancel_button.cancelbuttonfd
   2391   };
   2392 
   2393   if (NULL != cancelbutton_task)
   2394   {
   2395     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2396                 "Cancel task still active (!?), not starting another one\n");
   2397     return;
   2398   }
   2399   cancelbutton_task = GNUNET_SCHEDULER_add_read_file (
   2400     GNUNET_TIME_UNIT_FOREVER_REL,
   2401     &fh,
   2402     &cancel_button_pressed,
   2403     NULL);
   2404 }
   2405 
   2406 
   2407 /**
   2408  * @brief Send data to the vmc via the uart bus
   2409  *
   2410  * @param cls closure
   2411  */
   2412 static void
   2413 write_mdb_command (void *cls)
   2414 {
   2415   int did_write = 0;
   2416 
   2417   (void) cls;
   2418   mdb.wtask = NULL;
   2419 
   2420   if (disable_mdb)
   2421   {
   2422     /* check if there is a cmd to send and overwrite last command */
   2423     if (NULL == mdb.cmd)
   2424       return;
   2425     mdb.last_cmd = mdb.cmd;
   2426     mdb.cmd = NULL;
   2427     run_mdb_event_loop ();
   2428     return;
   2429   }
   2430   /* if command was sent partially, send the rest */
   2431   if (mdb.tx_off < mdb.tx_len)
   2432   {
   2433     ssize_t ret = write (mdb.uartfd,
   2434                          &mdb.txBuffer[mdb.tx_off],
   2435                          mdb.tx_len - mdb.tx_off);
   2436     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2437                 "Wrote %d bytes to MDB\n",
   2438                 (int) ret);
   2439     did_write = 1;
   2440     if (-1 == ret)
   2441     {
   2442       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   2443                                 "write",
   2444                                 uart_device_filename);
   2445       if (in_shutdown)
   2446         mdb_shutdown ();
   2447       else
   2448         GNUNET_SCHEDULER_shutdown ();
   2449       return;
   2450     }
   2451     mdb.tx_off += ret;
   2452     /* if command was sent sucessfully start the timer for ACK timeout */
   2453     if ( (ret > 0) &&
   2454          (mdb.tx_off == mdb.tx_len) )
   2455       mdb.ack_timeout = GNUNET_TIME_relative_to_absolute (MAX_ACK_LATENCY);
   2456   }
   2457   /* ongoing write incomplete, continue later */
   2458   if (mdb.tx_off < mdb.tx_len)
   2459   {
   2460     run_mdb_event_loop ();
   2461     return;
   2462   }
   2463   if (NULL != mdb.last_cmd)
   2464   {
   2465     struct GNUNET_TIME_Relative del;
   2466 
   2467     /* Still waiting for ACK! -> delay write task  */
   2468     del = GNUNET_TIME_absolute_get_remaining (mdb.ack_timeout);
   2469     if (0 != del.rel_value_us)
   2470     {
   2471       if (did_write)
   2472         run_mdb_event_loop ();
   2473       else
   2474         mdb.wtask = GNUNET_SCHEDULER_add_delayed (del,
   2475                                                   &write_mdb_command,
   2476                                                   NULL);
   2477       return;
   2478     }
   2479     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2480                 "MDB failed to acknowledge command `%s' within timeout\n",
   2481                 mdb.last_cmd->name);
   2482     mdb.last_cmd = NULL;
   2483     if (in_shutdown)
   2484     {
   2485       mdb_shutdown ();
   2486       return;
   2487     }
   2488   }
   2489   if (NULL == mdb.cmd)
   2490     return;
   2491   /* if there is a command to send calculate length and checksum */
   2492   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2493               "Sending command `%s'\n",
   2494               mdb.cmd->name);
   2495   mdb.tx_off = 0;
   2496   mdb.tx_len = mdb.cmd->cmd.bin_size + mdb.cmd->data.bin_size + 1;
   2497   GNUNET_assert (mdb.tx_len <= sizeof (mdb.txBuffer));
   2498   {
   2499     /* calculate checksum: sum up command and data, take just the LSB of the result */
   2500     uint32_t chkSum = 0;
   2501 
   2502     for (size_t idx = 0; idx < mdb.cmd->cmd.bin_size; idx++)
   2503       chkSum += mdb.txBuffer[idx] = mdb.cmd->cmd.bin[idx];
   2504     for (size_t idx = 0; idx < mdb.cmd->data.bin_size; idx++)
   2505       chkSum += mdb.txBuffer[idx + mdb.cmd->cmd.bin_size] =
   2506         mdb.cmd->data.bin[idx];
   2507     mdb.txBuffer[mdb.cmd->cmd.bin_size + mdb.cmd->data.bin_size] =
   2508       (uint8_t) (chkSum & 0xFF);
   2509   }
   2510   mdb.last_cmd = mdb.cmd;
   2511   mdb.cmd = NULL;
   2512   run_mdb_event_loop ();
   2513 }
   2514 
   2515 
   2516 /**
   2517  * @brief MDB acknowledged the last command, proceed.
   2518  */
   2519 static void
   2520 handle_ack ()
   2521 {
   2522   if (&cmd_begin_session == mdb.last_cmd)
   2523     mdb.session_running = true;
   2524   if (&cmd_deny_vend == mdb.last_cmd)
   2525   {
   2526     mdb.session_running = false;
   2527     mdb.cmd = &endSession;
   2528   }
   2529   if (&cmd_reader_display_sold_out == mdb.last_cmd)
   2530     mdb.cmd = &cmd_deny_vend;
   2531   if (&cmd_reader_display_internal_error == mdb.last_cmd)
   2532     mdb.cmd = &cmd_deny_vend;
   2533   if (&cmd_reader_display_backend_not_reachable == mdb.last_cmd)
   2534     mdb.cmd = &cmd_deny_vend;
   2535   mdb.last_cmd = NULL;
   2536   /* Cause the write-task to be re-scheduled now */
   2537   if (NULL != mdb.wtask)
   2538   {
   2539     GNUNET_SCHEDULER_cancel (mdb.wtask);
   2540     mdb.wtask = NULL;
   2541   }
   2542 }
   2543 
   2544 
   2545 /**
   2546  * @brief Parse received command from the VMC
   2547  *
   2548  * @param hex received command from VMC
   2549  * @param hex_len number of characters in @a hex
   2550  */
   2551 static void
   2552 handle_command (const char *hex,
   2553                 size_t hex_len)
   2554 {
   2555   unsigned int cmd;
   2556   unsigned int tmp = 0;
   2557   uint32_t chkSum;
   2558 
   2559   /* if the received command is 0 or not a multiple of 2 we cannot parse it */
   2560   if (0 == hex_len)
   2561     return;
   2562   if (0 != (hex_len % 2))
   2563   {
   2564     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2565                 "Received unexpected input `%.*s'\n",
   2566                 (int) hex_len,
   2567                 hex);
   2568     GNUNET_break_op (0);
   2569     return;
   2570   }
   2571   /* convert the received 2 bytes from ASCII to hex */
   2572   if (1 != sscanf (hex,
   2573                    "%2X",
   2574                    &cmd))
   2575   {
   2576     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2577                 "Received non-HEX input `%.*s'\n",
   2578                 (int) hex_len,
   2579                 hex);
   2580     GNUNET_break_op (0);
   2581     return;
   2582   }
   2583 
   2584   /* parse the first byte (cmd) and the second byte (subcmd) */
   2585   switch (cmd)
   2586   {
   2587   case VMC_VEND:
   2588     {
   2589       unsigned int subcmd;
   2590 
   2591       if (4 > hex_len)
   2592       {
   2593         GNUNET_break_op (0);
   2594         return;
   2595       }
   2596       if (1 != sscanf (&hex[2],
   2597                        "%2X",
   2598                        &subcmd))
   2599       {
   2600         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2601                     "Received non-HEX input `%.*s'\n",
   2602                     (int) hex_len - 2,
   2603                     &hex[2]);
   2604         GNUNET_break_op (0);
   2605         return;
   2606       }
   2607       switch (subcmd)
   2608       {
   2609       case VMC_VEND_REQUEST:
   2610         {
   2611           unsigned int product;
   2612 
   2613           /* Calculate the checksum and check it */
   2614           chkSum = cmd;
   2615 
   2616           for (size_t offset = 1; offset < ((hex_len / 2)); offset++)
   2617           {
   2618             chkSum += tmp;
   2619             if (1 != sscanf (hex + (2 * offset),
   2620                              "%2X",
   2621                              &tmp))
   2622             {
   2623               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2624                           "Received non-HEX input `%.*s'\n",
   2625                           (int) hex_len,
   2626                           hex);
   2627               GNUNET_break_op (0);
   2628               return;
   2629             }
   2630           }
   2631           if ( ((uint8_t) (chkSum & 0xFF)) != tmp)
   2632           {
   2633             mdb.cmd = &cmd_reader_display_internal_error;
   2634             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2635                         "Received command with wrong checksum `%.*s'\n",
   2636                         (int) hex_len,
   2637                         hex);
   2638             temporary_error ("err-num-read-fail");
   2639             break;
   2640           }
   2641 
   2642           GNUNET_break (mdb.session_running);
   2643           /* NOTE: hex[4..7] contain the price */
   2644           if (12 > hex_len)
   2645           {
   2646             GNUNET_break_op (0);
   2647             return;
   2648           }
   2649           if (1 != sscanf (&hex[8],
   2650                            "%4X",
   2651                            &product))
   2652           {
   2653             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2654                         "Received non-HEX input `%.*s'\n",
   2655                         (int) hex_len - 8,
   2656                         &hex[8]);
   2657             GNUNET_break_op (0);
   2658             return;
   2659           }
   2660           /* compare the received product number with the defined product numbers */
   2661           for (unsigned int i = 0; i < products_length; i++)
   2662             if (product == products[i].number)
   2663             {
   2664               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2665                           "Product %u selected on MDB\n",
   2666                           product);
   2667               if ( (sold_out_enabled) &&
   2668                    (products[i].sold_out) )
   2669               {
   2670                 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2671                             "Product %s sold out, denying vend\n",
   2672                             products[i].description);
   2673                 mdb.cmd = &cmd_reader_display_sold_out;
   2674                 temporary_error ("err-sold-out");
   2675                 return;
   2676               }
   2677               payment_activity = launch_payment (&products[i]);
   2678               if (NULL == payment_activity)
   2679                 vend_failure ();
   2680               return;
   2681             }
   2682           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2683                       "Unknown product %u selected on MDB, denying vend\n",
   2684                       product);
   2685           temporary_error ("unknown-product");
   2686           mdb.cmd = &cmd_deny_vend;
   2687           break;
   2688         }
   2689       case VMC_VEND_SUCCESS:
   2690         GNUNET_break (mdb.session_running);
   2691         vend_success ();
   2692         break;
   2693       case VMC_VEND_FAILURE:
   2694         {
   2695           GNUNET_break (mdb.session_running);
   2696           vend_failure ();
   2697           break;
   2698         }
   2699       case VMC_VEND_SESSION_COMPLETE:
   2700         {
   2701           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2702                       "Received MDB session complete\n");
   2703           mdb.session_running = false;
   2704           mdb.cmd = &endSession;
   2705           if (NULL != payment_activity)
   2706           {
   2707             cleanup_payment (payment_activity);
   2708             payment_activity = NULL;
   2709           }
   2710         }
   2711         break;
   2712       default:
   2713         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2714                     "Unknown MDB sub-command %X of command %X\n",
   2715                     subcmd,
   2716                     cmd);
   2717         break;
   2718       }
   2719       break;
   2720     }
   2721   case VMC_REVALUE:
   2722     {
   2723       unsigned int subcmd;
   2724 
   2725       if (4 > hex_len)
   2726       {
   2727         GNUNET_break_op (0);
   2728         return;
   2729       }
   2730       if (1 != sscanf (&hex[2],
   2731                        "%2X",
   2732                        &subcmd))
   2733       {
   2734         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2735                     "Received non-HEX input `%.*s'\n",
   2736                     (int) hex_len - 2,
   2737                     &hex[2]);
   2738         GNUNET_break_op (0);
   2739         return;
   2740       }
   2741       switch (subcmd)
   2742       {
   2743       case VMC_REVALUE_REQUEST:
   2744         {
   2745           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2746                       "Received request for revalue via MDB\n");
   2747           mdb.cmd = &cmd_revalue_approved;
   2748           break;
   2749         }
   2750       case VMC_REVALUE_LIMIT_REQUEST:
   2751         {
   2752           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2753                       "Received request for revalue limit amount via MDB\n");
   2754           mdb.cmd = &cmd_revalue_amount;
   2755           break;
   2756         }
   2757       default:
   2758         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2759                     "Unknown MDB sub-command %X of command %X\n",
   2760                     subcmd,
   2761                     cmd);
   2762         break;
   2763       }
   2764       break;
   2765     }
   2766   case VMC_CONF:
   2767     {
   2768       unsigned int subcmd;
   2769 
   2770       if (4 > hex_len)
   2771       {
   2772         GNUNET_break_op (0);
   2773         return;
   2774       }
   2775       if (1 != sscanf (&hex[2],
   2776                        "%2X",
   2777                        &subcmd))
   2778       {
   2779         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2780                     "Received non-HEX input `%.*s'\n",
   2781                     (int) hex_len - 2,
   2782                     &hex[2]);
   2783         GNUNET_break_op (0);
   2784         return;
   2785       }
   2786       switch (subcmd)
   2787       {
   2788       case VMC_READER_CONF:
   2789         {
   2790           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2791                       "Received request for configuration via MDB\n");
   2792           mdb.cmd = &cmd_reader_config_data;
   2793           break;
   2794 
   2795         }
   2796       case VMC_SETUP_MAX_MIN_PRICES:
   2797         {
   2798           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2799                       "Received max and min prices via MDB\n");
   2800           break;
   2801         }
   2802       default:
   2803         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2804                     "Unknown MDB sub-command %X of command %X\n",
   2805                     subcmd,
   2806                     cmd);
   2807         break;
   2808       }
   2809       break;
   2810     }
   2811   case VMC_POLL:
   2812     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2813                 "Received POLL from MDB (ignored)\n");
   2814     break;
   2815   case VMC_CMD_RESET:
   2816     {
   2817       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2818                   "Received RESET from MDB (ignored)\n");
   2819       break;
   2820     }
   2821   case VMC_READER:
   2822     {
   2823       unsigned int subcmd;
   2824 
   2825       if (4 > hex_len)
   2826       {
   2827         GNUNET_break_op (0);
   2828         return;
   2829       }
   2830       if (1 != sscanf (&hex[2],
   2831                        "%2X",
   2832                        &subcmd))
   2833       {
   2834         GNUNET_break_op (0);
   2835         return;
   2836       }
   2837 
   2838       switch (subcmd)
   2839       {
   2840       case VMC_READER_DISABLE:
   2841         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2842                     "Received Reader Disable via MDB\n");
   2843         mdb.session_running = false;
   2844         if (NULL != payment_activity)
   2845         {
   2846           cleanup_payment (payment_activity);
   2847           payment_activity = NULL;
   2848         }
   2849         for (unsigned int i = 0; i < products_length; i++)
   2850         {
   2851           if ( (sold_out_enabled) &&
   2852                (0 != strcmp (products[i].description,
   2853                              "empty")) &&
   2854                (products[i].sold_out) )
   2855           {
   2856             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2857                         "Resetting sold out state of product %s\n",
   2858                         products[i].description);
   2859             products[i].sold_out = false;
   2860           }
   2861         }
   2862         break;
   2863       case VMC_READER_ENABLE:
   2864         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2865                     "Received Reader Enable via MDB\n");
   2866         mdb.session_running = false;
   2867         break;
   2868       case VMC_READER_CANCEL:
   2869         mdb.cmd = &cmd_reader_cancelled;
   2870         mdb.session_running = false;
   2871         break;
   2872       default:
   2873         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2874                     "Unknown MDB sub-command %X of command %X\n",
   2875                     subcmd,
   2876                     cmd);
   2877         break;
   2878       }
   2879       break;
   2880     }
   2881   case VMC_REQUEST_ID:
   2882     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2883                 "Received VMC request ID, no need to handle (done by HW)\n");
   2884     break;
   2885   case VMC_ACKN:
   2886     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2887                 "Received acknowledgement (for command `%s') from MDB\n",
   2888                 (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
   2889     handle_ack ();
   2890     break;
   2891   case VMC_OOSQ:
   2892     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2893                 "Received command out of sequence from MDB (for command `%s')\n",
   2894                 (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
   2895     mdb.session_running = false;
   2896     if (mdb.last_cmd != &endSession)
   2897       mdb.cmd = &endSession;
   2898     else
   2899       mdb.last_cmd = NULL;
   2900     break;
   2901   case VMC_RETR:
   2902     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2903                 "Received request to resend previous data from MDB (previous command was `%s')\n",
   2904                 (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
   2905     GNUNET_break (NULL == mdb.cmd);
   2906     GNUNET_break (NULL != mdb.last_cmd);
   2907     mdb.cmd = mdb.last_cmd;
   2908     mdb.last_cmd = NULL;
   2909     break;
   2910   default:
   2911     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2912                 "Received unknown MDB command %X\n",
   2913                 cmd);
   2914     break;
   2915   }
   2916 }
   2917 
   2918 
   2919 /**
   2920  * @brief Read in the sent commands of the VMC controller
   2921  *
   2922  * @param cls closure
   2923  */
   2924 static void
   2925 read_mdb_command (void *cls)
   2926 {
   2927   ssize_t ret;
   2928   size_t cmdStartIdx;
   2929   size_t cmdEndIdx;
   2930 
   2931   (void) cls;
   2932   /* don't read if the mdb bus is disabled (only for testing) */
   2933   GNUNET_assert (! disable_mdb);
   2934   mdb.rtask = NULL;
   2935   ret = read (mdb.uartfd,
   2936               &mdb.rxBuffer[mdb.rx_off],
   2937               sizeof (mdb.rxBuffer) - mdb.rx_off);
   2938   if (-1 == ret)
   2939   {
   2940     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   2941                               "read",
   2942                               uart_device_filename);
   2943     GNUNET_SCHEDULER_shutdown ();
   2944     return;
   2945   }
   2946   mdb.rx_off += ret;
   2947 
   2948   while (mdb.rx_off > 0)
   2949   {
   2950     mdb_active = true;
   2951     /* find beginning of command */
   2952     for (cmdStartIdx = 0; cmdStartIdx < mdb.rx_off; cmdStartIdx++)
   2953       if (VMC_CMD_START == mdb.rxBuffer[cmdStartIdx])
   2954         break;
   2955     if (cmdStartIdx == mdb.rx_off)
   2956     {
   2957       mdb.rx_off = 0;
   2958       break;
   2959     }
   2960     /* find end of command */
   2961     for (cmdEndIdx = cmdStartIdx; cmdEndIdx < mdb.rx_off; cmdEndIdx++)
   2962       if (VMC_CMD_END == mdb.rxBuffer[cmdEndIdx])
   2963         break;
   2964     if (cmdEndIdx == mdb.rx_off)
   2965     {
   2966       /* check to make sure rxBuffer was big enough in principle */
   2967       if ( (cmdStartIdx == 0) &&
   2968            (mdb.rx_off == sizeof (mdb.rxBuffer)) )
   2969       {
   2970         /* Developer: if this happens, try increasing rxBuffer! */
   2971         GNUNET_break (0);
   2972         GNUNET_SCHEDULER_shutdown ();
   2973         return;
   2974       }
   2975       /* move cmd in buffer to the beginning of the buffer */
   2976       memmove (mdb.rxBuffer,
   2977                &mdb.rxBuffer[cmdStartIdx],
   2978                mdb.rx_off - cmdStartIdx);
   2979       mdb.rx_off -= cmdStartIdx;
   2980       break;
   2981     }
   2982     /* if the full command was received parse it */
   2983     handle_command ((const char *) &mdb.rxBuffer[cmdStartIdx + 1],
   2984                     cmdEndIdx - cmdStartIdx - 1);
   2985     /* move the data after the processed command to the left */
   2986     memmove (mdb.rxBuffer,
   2987              &mdb.rxBuffer[cmdEndIdx + 1],
   2988              mdb.rx_off - cmdEndIdx + 1);
   2989     mdb.rx_off -= (cmdEndIdx + 1);
   2990   }
   2991   if (in_shutdown)
   2992   {
   2993     mdb_shutdown ();
   2994     return;
   2995   }
   2996   run_mdb_event_loop ();
   2997 }
   2998 
   2999 
   3000 /**
   3001  * @brief Mdb event loop to start read and write tasks
   3002  */
   3003 static void
   3004 run_mdb_event_loop (void)
   3005 {
   3006   struct GNUNET_DISK_FileHandle fh = { mdb.uartfd };
   3007 
   3008   /* begin session if no cmd waits for sending and no cmd is received from the VMC */
   3009   if ( (! mdb.session_running) &&
   3010        (NULL == mdb.cmd) &&
   3011        (NULL == mdb.last_cmd) )
   3012   {
   3013     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3014                 "Beginning MDB session\n");
   3015     mdb.cmd = &cmd_begin_session;
   3016   }
   3017   /* start write task if he doesn't exist and if there is a cmd waiting to get sent */
   3018   if ( (NULL == mdb.wtask) &&
   3019        ( (NULL != mdb.cmd) ||
   3020          (in_shutdown) ||
   3021          (mdb.tx_len > mdb.tx_off) ) )
   3022   {
   3023     if (disable_mdb || ! mdb_active)
   3024       mdb.wtask = GNUNET_SCHEDULER_add_now (&write_mdb_command,
   3025                                             NULL);
   3026     else
   3027       mdb.wtask = GNUNET_SCHEDULER_add_write_file ((in_shutdown)
   3028                                                    ? SHUTDOWN_MDB_TIMEOUT
   3029                                                    :
   3030                                                    GNUNET_TIME_UNIT_FOREVER_REL,
   3031                                                    &fh,
   3032                                                    &write_mdb_command,
   3033                                                    NULL);
   3034   }
   3035   if ( (disable_mdb) &&
   3036        (NULL != mdb.last_cmd) )
   3037   {
   3038     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3039                 "Faking acknowledgement (for command `%s') from MDB\n",
   3040                 mdb.last_cmd->name);
   3041     handle_ack ();
   3042   }
   3043   /* start read task if he doesn't exist and the mdb communication is not disabled (only for testing) */
   3044   if ( (NULL == mdb.rtask) &&
   3045        (! disable_mdb) )
   3046     mdb.rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
   3047                                                 &fh,
   3048                                                 &read_mdb_command,
   3049                                                 NULL);
   3050 }
   3051 
   3052 
   3053 /**
   3054  * @brief Read the products from the configuration file
   3055  *
   3056  * @param cls closure in this case the name of the configuration file to read from
   3057  * @param section section of the config file to read from
   3058  */
   3059 static void
   3060 read_products (void *cls,
   3061                const char *section)
   3062 {
   3063   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   3064   struct Product tmpProduct = {
   3065     .sold_out = false
   3066   };
   3067   char *tmpKey;
   3068   char *thumbnail_fn;
   3069 
   3070   /* if the current section is not a product skip it */
   3071   if (0 != strncmp (section,
   3072                     "product-",
   3073                     strlen ("product-")))
   3074     return;
   3075   /* the current section is a product, parse its specifications and store it in a temporary product */
   3076   if (GNUNET_OK !=
   3077       GNUNET_CONFIGURATION_get_value_string (cfg,
   3078                                              section,
   3079                                              "DESCRIPTION",
   3080                                              &tmpProduct.description))
   3081   {
   3082     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3083                                section,
   3084                                "DESCRIPTION");
   3085     return;
   3086   }
   3087   if (sold_out_enabled)
   3088   {
   3089     if (0 == strcmp (tmpProduct.description,
   3090                      "empty"))
   3091     {
   3092       tmpProduct.sold_out = true;
   3093     }
   3094     else
   3095     {
   3096       tmpProduct.sold_out = false;
   3097     }
   3098   }
   3099   if (GNUNET_OK !=
   3100       TALER_config_get_amount (cfg,
   3101                                section,
   3102                                "price",
   3103                                &tmpProduct.price))
   3104   {
   3105     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3106                                section,
   3107                                "price");
   3108     GNUNET_free (tmpProduct.description);
   3109     return;
   3110   }
   3111   if (GNUNET_OK ==
   3112       GNUNET_CONFIGURATION_get_value_string (cfg,
   3113                                              section,
   3114                                              "KEY",
   3115                                              &tmpKey))
   3116   {
   3117     tmpProduct.key = tmpKey[0];
   3118     GNUNET_free (tmpKey);
   3119   }
   3120   else
   3121   {
   3122     /* no key */
   3123     tmpProduct.key = '\0';
   3124   }
   3125   if (GNUNET_OK !=
   3126       GNUNET_CONFIGURATION_get_value_string (cfg,
   3127                                              section,
   3128                                              "INSTANCE",
   3129                                              &tmpProduct.instance))
   3130     tmpProduct.instance = NULL;
   3131   tmpProduct.preview = NULL;
   3132   if (GNUNET_OK ==
   3133       GNUNET_CONFIGURATION_get_value_string (cfg,
   3134                                              section,
   3135                                              "THUMBNAIL",
   3136                                              &thumbnail_fn))
   3137   {
   3138     struct GNUNET_DISK_FileHandle *fh;
   3139 
   3140     fh = GNUNET_DISK_file_open (thumbnail_fn,
   3141                                 GNUNET_DISK_OPEN_READ,
   3142                                 GNUNET_DISK_PERM_NONE);
   3143     if (NULL == fh)
   3144     {
   3145       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3146                   "Could not open thumbnail `%s'\n",
   3147                   thumbnail_fn);
   3148     }
   3149     else
   3150     {
   3151       off_t flen;
   3152 
   3153       if (GNUNET_OK !=
   3154           GNUNET_DISK_file_handle_size (fh,
   3155                                         &flen))
   3156       {
   3157         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   3158                                   "stat",
   3159                                   thumbnail_fn);
   3160       }
   3161       else
   3162       {
   3163         void *thumb;
   3164         size_t len;
   3165         ssize_t ret;
   3166 
   3167         len = (size_t) flen;
   3168         thumb = GNUNET_malloc (len);
   3169         ret = GNUNET_DISK_file_read (fh,
   3170                                      thumb,
   3171                                      len);
   3172         if ( (ret < 0) ||
   3173              (len != ((size_t) ret)) )
   3174         {
   3175           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   3176                                     "read",
   3177                                     thumbnail_fn);
   3178         }
   3179         else
   3180         {
   3181           const char *mime_type = "";
   3182           const char *ext;
   3183           char *base64;
   3184 
   3185           ext = strrchr (thumbnail_fn, '.');
   3186           if (NULL != ext)
   3187           {
   3188             static const struct
   3189             {
   3190               const char *ext;
   3191               const char *mime;
   3192             } mimes[] = {
   3193               { ".png", "image/png" },
   3194               { ".jpg", "image/jpeg" },
   3195               { ".jpeg", "image/jpeg" },
   3196               { ".svg", "image/svg" },
   3197               { NULL, NULL }
   3198             };
   3199 
   3200             for (unsigned int i = 0; NULL != mimes[i].ext; i++)
   3201               if (0 == strcasecmp (mimes[i].ext,
   3202                                    ext))
   3203               {
   3204                 mime_type = mimes[i].mime;
   3205                 break;
   3206               }
   3207           }
   3208           (void) GNUNET_STRINGS_base64_encode (thumb,
   3209                                                len,
   3210                                                &base64);
   3211           GNUNET_asprintf (&tmpProduct.preview,
   3212                            "data:%s;base64,%s",
   3213                            mime_type,
   3214                            base64);
   3215           GNUNET_free (base64);
   3216         }
   3217         GNUNET_free (thumb);
   3218       }
   3219       GNUNET_break (GNUNET_OK ==
   3220                     GNUNET_DISK_file_close (fh));
   3221     }
   3222     GNUNET_free (thumbnail_fn);
   3223   }
   3224   if (GNUNET_OK !=
   3225       GNUNET_CONFIGURATION_get_value_number (cfg,
   3226                                              section,
   3227                                              "NUMBER",
   3228                                              &tmpProduct.number))
   3229   {
   3230     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3231                                section,
   3232                                "NUMBER");
   3233     GNUNET_free (tmpProduct.description);
   3234     GNUNET_free (tmpProduct.instance);
   3235     GNUNET_free (tmpProduct.preview);
   3236     return;
   3237   }
   3238 
   3239   {
   3240     char *auth;
   3241 
   3242     if ( (GNUNET_OK !=
   3243           GNUNET_CONFIGURATION_get_value_string (cfg,
   3244                                                  section,
   3245                                                  "BACKEND_AUTHORIZATION",
   3246                                                  &auth)) &&
   3247          (GNUNET_OK !=
   3248           GNUNET_CONFIGURATION_get_value_string (cfg,
   3249                                                  "taler-mdb",
   3250                                                  "BACKEND_AUTHORIZATION",
   3251                                                  &auth)) )
   3252     {
   3253       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3254                                  section,
   3255                                  "BACKEND_AUTHORIZATION");
   3256       GNUNET_free (tmpProduct.description);
   3257       GNUNET_free (tmpProduct.instance);
   3258       GNUNET_free (tmpProduct.preview);
   3259       return;
   3260     }
   3261     GNUNET_asprintf (&tmpProduct.auth_header,
   3262                      "%s: %s",
   3263                      MHD_HTTP_HEADER_AUTHORIZATION,
   3264                      auth);
   3265     GNUNET_free (auth);
   3266   }
   3267   /* append the temporary product to the existing products */
   3268   GNUNET_array_append (products,
   3269                        products_length,
   3270                        tmpProduct);
   3271 }
   3272 
   3273 
   3274 /**
   3275  * @brief Initialise the uart device to send mdb commands
   3276  *
   3277  * @return #GNUNET_OK on success
   3278  */
   3279 static enum GNUNET_GenericReturnValue
   3280 mdb_init (void)
   3281 {
   3282   struct termios uart_opts_raw;
   3283 
   3284   if (disable_mdb)
   3285   {
   3286     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3287                 "Running with MDB disabled!\n");
   3288     run_mdb_event_loop ();
   3289     return GNUNET_OK;
   3290   }
   3291   /* open uart connection */
   3292   if (0 > (mdb.uartfd = open (uart_device_filename,
   3293                               O_RDWR | O_NOCTTY | O_NDELAY)))
   3294   {
   3295     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   3296                               "open",
   3297                               uart_device_filename);
   3298     return GNUNET_SYSERR;
   3299   }
   3300 
   3301   if (0 != tcgetattr (mdb.uartfd,
   3302                       &mdb.uart_opts_backup))
   3303   {
   3304     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
   3305                          "tcgetattr");
   3306     return GNUNET_SYSERR;
   3307   }
   3308   uart_opts_raw = mdb.uart_opts_backup;
   3309 
   3310   /* reset current uart discipline */
   3311   memset (&uart_opts_raw,
   3312           0,
   3313           sizeof(uart_opts_raw));
   3314 
   3315   /* set baudrate */
   3316   cfsetispeed (&uart_opts_raw, B9600);
   3317   cfsetospeed (&uart_opts_raw, B9600);
   3318 
   3319   /* set options */
   3320   uart_opts_raw.c_cflag &= ~PARENB;
   3321   uart_opts_raw.c_cflag &= ~CSTOPB;
   3322   uart_opts_raw.c_cflag &= ~CSIZE;
   3323   uart_opts_raw.c_cflag |= CS8;
   3324 
   3325   /* 19200 bps, 8 databits, ignore cd-signal, allow reading */
   3326   uart_opts_raw.c_cflag |= (CLOCAL | CREAD);
   3327   uart_opts_raw.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
   3328   uart_opts_raw.c_iflag = IGNPAR;
   3329   uart_opts_raw.c_oflag &= ~OPOST;
   3330   uart_opts_raw.c_cc[VMIN]  = 0;
   3331   uart_opts_raw.c_cc[VTIME] = 50;
   3332   tcflush (mdb.uartfd, TCIOFLUSH);
   3333   if (0 != tcsetattr (mdb.uartfd,
   3334                       TCSAFLUSH,
   3335                       &uart_opts_raw))
   3336   {
   3337     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
   3338                          "tcsetattr");
   3339     return GNUNET_SYSERR;
   3340   }
   3341   /* if the configuration of the uart was successful start the mdb write and read tasks */
   3342   run_mdb_event_loop ();
   3343   return GNUNET_OK;
   3344 }
   3345 
   3346 
   3347 /**
   3348  * @brief Start the application
   3349  *
   3350  * @param cls closure
   3351  * @param args arguments left
   3352  * @param cfgfile config file name
   3353  * @param cfg handle for the configuration
   3354  */
   3355 static void
   3356 run (void *cls,
   3357      char *const *args,
   3358      const char *cfgfile,
   3359      const struct GNUNET_CONFIGURATION_Handle *cfg)
   3360 {
   3361   (void) cls;
   3362   (void) args;
   3363   (void) cfgfile;
   3364 
   3365   /* parse the devices, if no config entry is found, a standard is used */
   3366   if (GNUNET_OK !=
   3367       GNUNET_CONFIGURATION_get_value_filename (cfg,
   3368                                                "taler-mdb",
   3369                                                "UART_DEVICE",
   3370                                                &uart_device_filename))
   3371   {
   3372     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3373                                "taler-mdb",
   3374                                "UART_DEVICE");
   3375     uart_device_filename = GNUNET_strdup ("/dev/ttyAMA0");
   3376   }
   3377   if (GNUNET_OK !=
   3378       GNUNET_CONFIGURATION_get_value_filename (cfg,
   3379                                                "taler-mdb",
   3380                                                "FRAMEBUFFER_DEVICE",
   3381                                                &framebuffer_device_filename))
   3382   {
   3383     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
   3384                                "taler-mdb",
   3385                                "FRAMEBUFFER_DEVICE");
   3386     framebuffer_device_filename = GNUNET_strdup ("/dev/fb1");
   3387   }
   3388   if (GNUNET_OK !=
   3389       GNUNET_CONFIGURATION_get_value_filename (cfg,
   3390                                                "taler-mdb",
   3391                                                "FRAMEBUFFER_BACKLIGHT",
   3392                                                &framebuffer_backlight_filename))
   3393   {
   3394     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3395                                "taler-mdb",
   3396                                "FRAMEBUFFER_BACKLIGHT");
   3397     framebuffer_backlight_filename = GNUNET_strdup (
   3398       "/sys/class/backlight/soc:backlight/brightness");
   3399   }
   3400   /* parse the taler configurations */
   3401   if (GNUNET_OK !=
   3402       GNUNET_CONFIGURATION_get_value_string (cfg,
   3403                                              "taler-mdb",
   3404                                              "BACKEND_BASE_URL",
   3405                                              &backend_base_url))
   3406   {
   3407     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3408                                "taler-mdb",
   3409                                "BACKEND_BASE_URL");
   3410     global_ret = EXIT_FAILURE;
   3411     return;
   3412   }
   3413   (void) GNUNET_CONFIGURATION_get_value_string (cfg,
   3414                                                 "taler-mdb",
   3415                                                 "ADVERTISEMENT_COMMAND",
   3416                                                 &adv_process_command);
   3417   (void) GNUNET_CONFIGURATION_get_value_string (cfg,
   3418                                                 "taler-mdb",
   3419                                                 "FAIL_COMMAND",
   3420                                                 &err_process_command);
   3421   if (GNUNET_OK !=
   3422       GNUNET_CONFIGURATION_get_value_string (cfg,
   3423                                              "taler-mdb",
   3424                                              "FULFILLMENT_MSG",
   3425                                              &fulfillment_msg))
   3426   {
   3427     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3428                                "taler-mdb",
   3429                                "FULFILLMENT_MSG");
   3430     global_ret = EXIT_FAILURE;
   3431     return;
   3432   }
   3433 
   3434   /* parse the products */
   3435   GNUNET_CONFIGURATION_iterate_sections (cfg,
   3436                                          &read_products,
   3437                                          (void *) cfg);
   3438   if (NULL == products)
   3439   {
   3440     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3441                 "No valid products configured\n");
   3442     global_ret = EXIT_NOTCONFIGURED;
   3443     return;
   3444   }
   3445 
   3446   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
   3447                                  NULL);
   3448 
   3449   /* initialize mdb */
   3450   if (GNUNET_OK != mdb_init ())
   3451   {
   3452     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3453                 "Unable to initialize MDB (mdb_init() failed)\n");
   3454     global_ret = EXIT_FAILURE;
   3455     GNUNET_SCHEDULER_shutdown ();
   3456     return;
   3457   }
   3458 
   3459   /* initialize nfc */
   3460   nfc_init (&context);
   3461   if (NULL == context)
   3462   {
   3463     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3464                 "Unable to initialize NFC (nfc_init() failed)\n");
   3465     global_ret = EXIT_FAILURE;
   3466     GNUNET_SCHEDULER_shutdown ();
   3467     return;
   3468   }
   3469 
   3470   /* open gpio pin for cancel button */
   3471   {
   3472     int efd;
   3473 
   3474     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3475                 "Exporting GPIO pin 23\n");
   3476     efd = open ("/sys/class/gpio/export",
   3477                 O_WRONLY);
   3478     if (-1 == efd)
   3479     {
   3480       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3481                   "Unable to open /gpio/export for cancel button\n");
   3482     }
   3483     else
   3484     {
   3485       if (2 != write (efd,
   3486                       "23",
   3487                       2))
   3488       {
   3489         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
   3490                                   "write",
   3491                                   "/sys/class/gpio/export");
   3492       }
   3493       GNUNET_assert (0 == close (efd));
   3494     }
   3495   }
   3496 
   3497   /* set direction: input */
   3498   {
   3499     int dfd;
   3500 
   3501     dfd = open ("/sys/class/gpio/gpio23/direction",
   3502                 O_WRONLY);
   3503     if (-1 == dfd)
   3504     {
   3505       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3506                   "Unable to open /gpio/gpio23/direction for cancel button\n");
   3507     }
   3508     else
   3509     {
   3510       if (2 != write (dfd,
   3511                       "in",
   3512                       2))
   3513       {
   3514         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
   3515                                   "write",
   3516                                   "/sys/class/gpio/gpio23/direction");
   3517       }
   3518       GNUNET_assert (0 == close (dfd));
   3519     }
   3520   }
   3521 
   3522   {
   3523     /* actually open fd for reading the state */
   3524     cancel_button.cancelbuttonfd = open ("/sys/class/gpio/gpio23/value",
   3525                                          O_RDONLY);
   3526     if (-1 == cancel_button.cancelbuttonfd)
   3527     {
   3528       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3529                   "Unable to open /gpio/gpio23/value for cancel button\n");
   3530     }
   3531   }
   3532 
   3533 
   3534 #if HAVE_QRENCODE_H
   3535   /* open the framebuffer device */
   3536   qrDisplay.devicefd = open (framebuffer_device_filename,
   3537                              O_RDWR);
   3538   if (-1 != qrDisplay.devicefd)
   3539   {
   3540     /* read information about the screen */
   3541     ioctl (qrDisplay.devicefd,
   3542            FBIOGET_VSCREENINFO,
   3543            &qrDisplay.var_info);
   3544 
   3545     /* store current screeninfo for reset */
   3546     qrDisplay.orig_vinfo = qrDisplay.var_info;
   3547 
   3548     if (16 != qrDisplay.var_info.bits_per_pixel)
   3549     {
   3550       /* Change variable info to 16 bit per pixel */
   3551       qrDisplay.var_info.bits_per_pixel = 16;
   3552       if (0 > ioctl (qrDisplay.devicefd,
   3553                      FBIOPUT_VSCREENINFO,
   3554                      &qrDisplay.var_info))
   3555       {
   3556         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   3557                              "ioctl(FBIOPUT_VSCREENINFO)");
   3558         return;
   3559       }
   3560     }
   3561 
   3562     /* Get fixed screen information */
   3563     if (0 > ioctl (qrDisplay.devicefd,
   3564                    FBIOGET_FSCREENINFO,
   3565                    &qrDisplay.fix_info))
   3566     {
   3567       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   3568                            "ioctl(FBIOGET_FSCREENINFO)");
   3569       return;
   3570     }
   3571 
   3572     /* get pointer onto frame buffer */
   3573     qrDisplay.memory = mmap (NULL,
   3574                              qrDisplay.fix_info.smem_len,
   3575                              PROT_READ | PROT_WRITE, MAP_SHARED,
   3576                              qrDisplay.devicefd,
   3577                              0);
   3578     if (MAP_FAILED == qrDisplay.memory)
   3579     {
   3580       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
   3581                            "mmap");
   3582       return;
   3583     }
   3584 
   3585     /* set the screen to white */
   3586     memset (qrDisplay.memory,
   3587             0xFF,
   3588             qrDisplay.var_info.xres * qrDisplay.var_info.yres
   3589             * sizeof (uint16_t));
   3590 
   3591     /* open backlight file to turn display backlight on and off */
   3592     qrDisplay.backlightfd = open (
   3593       framebuffer_backlight_filename, O_WRONLY);
   3594     if (0 > qrDisplay.backlightfd)
   3595     {
   3596       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
   3597                                 "open",
   3598                                 framebuffer_backlight_filename);
   3599     }
   3600     else
   3601     {
   3602       /* if '-i' flag was set, invert the on and off values */
   3603       if (backlight_invert)
   3604       {
   3605         backlight_on = '0';
   3606         backlight_off = '1';
   3607       }
   3608       /* turn off the backlight */
   3609       (void) ! write (qrDisplay.backlightfd,
   3610                       &backlight_off,
   3611                       1);
   3612     }
   3613   }
   3614   else
   3615   {
   3616     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
   3617                               "open",
   3618                               framebuffer_device_filename);
   3619   }
   3620 #endif
   3621   start_advertising ();
   3622   if (! disable_tty)
   3623     start_read_keyboard ();
   3624 }
   3625 
   3626 
   3627 /**
   3628  * @brief Convert the ASCII cmd in @hex to hex and store it in the mdb data struct @blk
   3629  *
   3630  * @param *blk pointer with reference to the mdb datablock
   3631  * @param *hex pointer to the string containing the cmd data
   3632  */
   3633 static void
   3634 parse_block (struct MdbBlock *blk,
   3635              const char *hex)
   3636 {
   3637   if (NULL == hex)
   3638   {
   3639     blk->bin_size = 0;
   3640     blk->bin = NULL;
   3641     return;
   3642   }
   3643   blk->bin_size = strlen (hex) / 2;
   3644   blk->bin = GNUNET_malloc (blk->bin_size);
   3645   for (size_t idx = 0; idx < blk->bin_size; idx++)
   3646   {
   3647     unsigned int val;
   3648 
   3649     GNUNET_assert (1 ==
   3650                    sscanf (&hex[idx * 2],
   3651                            "%2X",
   3652                            &val));
   3653     blk->bin[idx] = (uint8_t) val;
   3654   }
   3655 }
   3656 
   3657 
   3658 /**
   3659  * @brief Create a new mdb command
   3660  *
   3661  * @param *name pointer to the string containing the command name
   3662  * @param *cmd pointer to the string containing the command
   3663  * @param *data pointer to the string containing the command data
   3664  * @return structure of type MdbCommand holding the given information by the parameters
   3665  */
   3666 static struct MdbCommand
   3667 setup_mdb_cmd (const char *name,
   3668                const char *cmd,
   3669                const char *data)
   3670 {
   3671   struct MdbCommand cmdNew;
   3672 
   3673   cmdNew.name = (NULL == name)
   3674                 ? "No Cmd Name Set"
   3675                 : name;
   3676   parse_block (&cmdNew.cmd, cmd);
   3677   parse_block (&cmdNew.data, data);
   3678   return cmdNew;
   3679 }
   3680 
   3681 
   3682 int
   3683 main (int argc,
   3684       char*const*argv)
   3685 {
   3686   struct termios tty_opts_backup;
   3687   struct termios tty_opts_raw;
   3688   int have_tty;
   3689   int ret;
   3690   struct GNUNET_GETOPT_CommandLineOption options[] = {
   3691     GNUNET_GETOPT_option_flag ('d',
   3692                                "disable-mdb",
   3693                                "disable all interactions with the MDB (for testing without machine)",
   3694                                &disable_mdb),
   3695     GNUNET_GETOPT_option_flag ('i',
   3696                                "backlight-invert",
   3697                                "invert the backlight on/off values (standard on = 1)",
   3698                                &backlight_invert),
   3699     GNUNET_GETOPT_option_flag ('s',
   3700                                "enable-soldout",
   3701                                "enable detection of sold-out products, preventing vend operations of the respective product until the process is restarted",
   3702                                &sold_out_enabled),
   3703     GNUNET_GETOPT_option_flag ('t',
   3704                                "disable-tty",
   3705                                "disable all keyboard interactions (for running from systemd)",
   3706                                &disable_tty),
   3707     GNUNET_GETOPT_OPTION_END
   3708   };
   3709 
   3710   if (! disable_tty)
   3711   {
   3712     have_tty = isatty (STDIN_FILENO);
   3713     if (have_tty)
   3714     {
   3715       if (0 != tcgetattr (STDIN_FILENO, &tty_opts_backup))
   3716         fprintf (stderr,
   3717                  "Failed to get terminal discipline\n");
   3718       tty_opts_raw = tty_opts_backup;
   3719       tty_opts_raw.c_lflag &= ~(ECHO | ECHONL | ICANON);
   3720       if (0 != tcsetattr (STDIN_FILENO, TCSANOW, &tty_opts_raw))
   3721         fprintf (stderr,
   3722                  "Failed to set terminal discipline\n");
   3723     }
   3724   }
   3725   /* make the needed commands for the communication with the vending machine controller */
   3726   cmd_reader_config_data = setup_mdb_cmd ("Reader Config",
   3727                                           READER_CONFIG,
   3728                                           READER_FEATURE_LEVEL
   3729                                           READER_COUNTRYCODE
   3730                                           READER_SCALE_FACTOR
   3731                                           READER_DECIMAL_PLACES
   3732                                           READER_MAX_RESPONSE_TIME
   3733                                           READER_MISC_OPTIONS);
   3734   cmd_begin_session = setup_mdb_cmd ("Begin Session",
   3735                                      READER_BEGIN_SESSION,
   3736                                      READER_FUNDS_AVAILABLE);
   3737   cmd_approve_vend = setup_mdb_cmd ("Approve Vend",
   3738                                     READER_VEND_APPROVE,
   3739                                     READER_VEND_AMOUNT);
   3740   cmd_reader_cancelled = setup_mdb_cmd ("Confirm cancellation",
   3741                                         READER_CANCELLED,
   3742                                         NULL);
   3743   cmd_deny_vend = setup_mdb_cmd ("Deny Vend",
   3744                                  READER_VEND_DENIED,
   3745                                  NULL);
   3746   endSession = setup_mdb_cmd ("End Session",
   3747                               READER_END_SESSION,
   3748                               NULL);
   3749   cmd_revalue_approved = setup_mdb_cmd ("Reader Approve Revalue",
   3750                                         READER_REVALUE_APPROVED,
   3751                                         NULL);
   3752 
   3753   cmd_revalue_amount = setup_mdb_cmd ("Send Revalue Limit Amount",
   3754                                       READER_REVALUE_LIMIT,
   3755                                       READER_REVALUE_LIMIT_AMOUNT);
   3756 
   3757   cmd_reader_NACK = setup_mdb_cmd ("Reader NACK",
   3758                                    READER_NACK,
   3759                                    NULL);
   3760 
   3761   cmd_reader_display_sold_out = setup_mdb_cmd ("Display Sold Out",
   3762                                                READER_DISPLAY_REQUEST,
   3763                                                READER_DISPLAY_REQUEST_TIME
   3764                                                READER_DISPLAY_SOLD_OUT);
   3765 
   3766   cmd_reader_display_internal_error = setup_mdb_cmd (
   3767     "Display Communication Error",
   3768     READER_DISPLAY_REQUEST,
   3769     READER_DISPLAY_REQUEST_TIME
   3770     READER_DISPLAY_INTERNAL_ERROR);
   3771 
   3772   cmd_reader_display_backend_not_reachable = setup_mdb_cmd (
   3773     "Display Backend not reachable",
   3774     READER_DISPLAY_REQUEST,
   3775     READER_DISPLAY_REQUEST_TIME
   3776     READER_DISPLAY_BACKEND_NOT_REACHABLE);
   3777   ret = GNUNET_PROGRAM_run (TALER_MDB_project_data (),
   3778                             argc,
   3779                             argv,
   3780                             "taler-mdb",
   3781                             "This is an application for snack machines to pay with GNU Taler via NFC.\n",
   3782                             options,
   3783                             &run,
   3784                             NULL);
   3785   if (! disable_tty)
   3786   {
   3787     if (have_tty)
   3788     {
   3789       /* Restore previous TTY settings */
   3790       if (0 != tcsetattr (STDIN_FILENO,
   3791                           TCSANOW,
   3792                           &tty_opts_backup))
   3793         fprintf (stderr,
   3794                  "Failed to restore terminal discipline\n");
   3795     }
   3796   }
   3797   if (GNUNET_NO == ret)
   3798     return 0;
   3799   if (GNUNET_OK != ret)
   3800     return 1;
   3801   return global_ret;
   3802 }