taler-mdb

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

taler-mdb.c (104011B)


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