taler-mdb

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

commit ca815f5c4cca08d5b1a2b259914db67bbf9e5e2e
parent c232406660abc851c790246b085bc5ae181df926
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  9 Nov 2019 11:51:02 +0100

use event loop

Diffstat:
Msrc/Makefile.am | 5+----
Msrc/communication.c | 145-------------------------------------------------------------------------------
Msrc/configuration.h | 20--------------------
Msrc/main.c | 725++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/nfc.c | 100-------------------------------------------------------------------------------
Msrc/product.c | 122-------------------------------------------------------------------------------
Msrc/product.h | 10----------
Msrc/wallet.c | 82-------------------------------------------------------------------------------
8 files changed, 552 insertions(+), 657 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am @@ -7,14 +7,11 @@ if USE_COVERAGE endif taler_nfc_SOURCES = \ - nfc.c nfc.h \ - wallet.c wallet.h \ - product.c product.h \ - communication.c communication.h \ main.c taler_nfc_LDADD = \ -ltalermerchant \ -ltalerutil \ + -lgnunetcurl \ -lgnunetutil \ -ljansson \ -lnfc \ diff --git a/src/communication.c b/src/communication.c @@ -33,151 +33,6 @@ along with #include "wallet.h" #include "configuration.h" -int SNACK_taler_init (CURL **curl) -{ - CURLcode result; - - /* initialize curl */ - result = curl_global_init (CURL_GLOBAL_ALL); - *curl = curl_easy_init (); - if ( ! (*curl) ||(result != CURLE_OK)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_taler_init: could not inizialize curl handle\n"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -void SNACK_taler_exit (CURL **curl) -{ - curl_easy_cleanup (*curl); - curl_global_cleanup (); -} - -static size_t _SNACK_taler_write_response (void *contents, size_t size, size_t - nmemb, - void *userp) -{ - size_t realSize = size * nmemb; - struct TalerResponse *response = (struct TalerResponse *) userp; - - response->string = GNUNET_realloc (response->string, response->size - + realSize + 1); - - memcpy (&(response->string[response->size]), contents, realSize); - response->size += realSize; - response->string[response->size] = 0; - - return realSize; -} - -int SNACK_taler_create_order_req (ProductOrder *product) -{ - json_t *orderReq; - - /* create the string representing the amount, e.g. "KUDOS:2.5" ( +2 because of ':' and '\0' ) */ - char amountStr[ strlen (product->talerCfg->currency) + strlen ( - product->amount) + 2 ]; - /* create the fulfillment ur, e.g. "taler://fulfillment-success/Enjoy+your+ice+cream!"; ( +2 because of '!' and '\0' ) */ - char fulfillmentUrl[ strlen (product->talerCfg->fulfillmentUrl) + strlen ( - product->talerCfg->fulfillmentMsg) - + strlen (product->product) + 2 ]; - - /* create the strings declared earlier */ - sprintf (amountStr, "%s:%s", product->talerCfg->currency, product->amount); - sprintf (fulfillmentUrl, "%s%s%s!", product->talerCfg->fulfillmentUrl, - product->talerCfg->fulfillmentMsg, - product->product); - - /* create the json object for the order request */ - orderReq = json_pack ("{ s: { s:s, s:s, s:s, s:s }}", SNACK_JSON_REQ_ORDER, - SNACK_JSON_REQ_SUMMARY, product->product, - SNACK_JSON_REQ_AMOUNT, amountStr, - SNACK_JSON_REQ_FULFILLMENT, fulfillmentUrl, - SNACK_JSON_REQ_NONCE, product->nonce); - if ( ! orderReq ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_taler_create_order_req: error creating json object\n"); - return EXIT_FAILURE; - } - - /* create the string to send to taler in compact format */ - char *buffer = json_dumps (orderReq, JSON_COMPACT); - if ( ! buffer ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_taler_create_order_req: error converting json to string\n"); - return EXIT_FAILURE; - } - - /* allocate memory for the string and copy it into the product */ - product->orderRequest = GNUNET_realloc (product->orderRequest, strlen ( - buffer) + 1); - strcpy (product->orderRequest, buffer); - - /* print the created order request and free the json object */ - printf ("Created order Request: \n"); - json_dumpf (orderReq, stdout, JSON_INDENT (2) ); - printf ("\n"); - json_decref (orderReq); - - /* free the allocated buffer from json_dumps */ - free (buffer); - - return EXIT_SUCCESS; -} - -int SNACK_taler_create_order (CURL *curl, ProductOrder *product) -{ - CURLcode result; - struct curl_slist *list = NULL; - char url[ strlen (product->talerCfg->backendBaseUrl) + strlen ( - SNACK_TALER_ORDER) + 1 ]; - - /* reset the response size */ - product->response->size = 0; - - /* set the url */ - sprintf (url, "%s%s", product->talerCfg->backendBaseUrl, SNACK_TALER_ORDER); - curl_easy_setopt (curl, CURLOPT_URL, url); - - /* set the authentication headers */ - list = curl_slist_append (list, product->talerCfg->authorization); - curl_easy_setopt (curl, CURLOPT_HTTPHEADER, list); - - /* curl option "post" */ - curl_easy_setopt (curl, CURLOPT_HTTPPOST, true); - curl_easy_setopt (curl, CURLOPT_POSTFIELDS, product->orderRequest); - - /* pass the write function and the struct to write to */ - curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, _SNACK_taler_write_response); - curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *) product->response); - - /* perform the call */ - result = curl_easy_perform (curl); - if ( result != CURLE_OK ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_taler_create_order: could not communicate with \"%s\"\n", - url); - curl_slist_free_all (list); - return EXIT_FAILURE; - } - - /* reset the curl options */ - curl_easy_reset (curl); - - /* clean up */ - curl_slist_free_all (list); - - - printf ("%s\n", product->response->string); - - return EXIT_SUCCESS; -} int SNACK_taler_check_payment_status (CURL *curl, ProductOrder *product) { diff --git a/src/configuration.h b/src/configuration.h @@ -42,41 +42,21 @@ along with #define FULLFILLMENT_URL "fulfillment-url" #define FULLFILLMENT_MSG "fulfillment-msg" -/* default config file */ -#define SNACK_DEFAULT_CONFIG_FILE "taler-mdb.conf" /* taler url extensions */ #define SNACK_TALER_ORDER "/order" #define SNACK_TALER_CHECK "/check-payment" #define SNACK_TALER_ORDER_CHECK "?order_id=" -/* curl auth header */ -#define SNACK_CURL_AUTH_HEADER "Authorization" /* json order check keys */ #define SNACK_JSON_PAID "paid" #define SNACK_JSON_PAY_URI "taler_pay_uri" #define SNACK_JSON_ORDER_ID "order_id" -/* json order request keys */ -#define SNACK_JSON_REQ_ORDER "order" -#define SNACK_JSON_REQ_SUMMARY "summary" -#define SNACK_JSON_REQ_AMOUNT "amount" -#define SNACK_JSON_REQ_FULFILLMENT "fulfillment_url" -#define SNACK_JSON_REQ_NONCE "nonce" /* nonce definitions */ #define NONCE_SIZE 32 -/* Wallet AID */ -static const uint8_t taler_aid[] = { 0xF0, 0x00, 0x54, 0x41, 0x4c, 0x45, 0x52 }; - -/* APDU commands */ -#define APDU_SUCCESS "\x90\x00" -static const uint8_t select_file[] = { 0x00, 0xA4, 0x04, 0x00, 0x07 }; -static const uint8_t put_data[] = { 0x00, 0xDA, 0x01, 0x00, 0x7c, 0x01 }; - -/* tunneling */ -static const uint8_t get_data[] = { 0x00, 0xCA, 0x01, 0x00, 0x00, 0x00 }; #endif // URL_H diff --git a/src/main.c b/src/main.c @@ -33,79 +33,164 @@ along with #include <gnunet/gnunet_util_lib.h> #include <taler/taler_merchant_service.h> -#include "nfc.h" -#include "communication.h" -#include "configuration.h" -#include "product.h" +#define BACKEND_POLL_TIMEOUT GNUNET_TIME_UNIT_MINUTES -static int global_ret; +#define APDU_SUCCESS "\x90\x00" -static struct GNUNET_SCHEDULER_Task *super_task; +/* json order request keys */ +#define SNACK_JSON_REQ_ORDER "order" +#define SNACK_JSON_REQ_SUMMARY "summary" +#define SNACK_JSON_REQ_AMOUNT "amount" +#define SNACK_JSON_REQ_FULFILLMENT "fulfillment_url" -static struct GNUNET_CURL_Context *ctx; +/* default config file */ +#define SNACK_DEFAULT_CONFIG_FILE "taler-mdb.conf" + +/* curl auth header */ +#define SNACK_CURL_AUTH_HEADER "Authorization" + + +/* Wallet AID */ +static const uint8_t taler_aid[] = { 0xF0, 0x00, 0x54, 0x41, 0x4c, 0x45, 0x52 }; + +/* APDU commands */ +static const uint8_t select_file[] = { 0x00, 0xA4, 0x04, 0x00, 0x07 }; +static const uint8_t put_data[] = { 0x00, 0xDA, 0x01, 0x00, 0x7c, 0x01 }; + +/* tunneling */ +static const uint8_t get_data[] = { 0x00, 0xCA, 0x01, 0x00, 0x00, 0x00 }; -static struct GNUNET_CURL_RescheduleContext *; + +struct Product +{ + const char *price; + const char *description; + char key; +}; struct PaymentActivity { - ProductOrder product; struct TALER_MERCHANT_PollPaymentOperation *ppo; struct TALER_MERCHANT_ProposalOperation *po; + struct TALER_MERCHANT_CheckPaymentOperation *cpo; + + char *taler_pay_uri; + nfc_device *pnd; + + nfc_target nt; + + struct GNUNET_SCHEDULER_Task *task; }; -nfc_context *context; +static nfc_context *context; + +static int global_ret; + +static struct GNUNET_SCHEDULER_Task *keyboard_task; + +static struct GNUNET_CURL_Context *ctx; + +static struct GNUNET_CURL_RescheduleContext *rc; + +static char *currency; + +static char *backendBaseUrl; + +static char *fulfillmentUrl; + +static char *fulfillmentMsg; + +static char *authorization; static struct PaymentActivity *payment_activity; -static void * -start_nfc_transmission (void *ignored) -{ - /* supress warning about unused variable */ - (void) ignored; - if (pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL) != 0) +/** + * FIXME: read from configuration file instead! + * GNUNET_CONFIGURATION_* iteration over values. + */ +static struct Product products[] = { + { + .price = "0.1", + .description = "Snickers", + .key = 's' + }, { - printf ("Error setting thread cancelling type\n"); + .price = "0.1", + .description = "Twix", + .key = 't' + }, + { + .price = NULL, + .description = NULL, + .key = '\0' } +}; - /* start endless transmission loop (until threads gets cancelled) */ - while (1) - { - if ( pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL) != 0 ) - { - printf ("Error setting thread cancelling state\n"); - } - SNACK_nfc_transmit (context, product.payUrl, strlen (product.payUrl) ); - if ( pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL) != 0) - { - printf ("Error setting thread cancelling state\n"); - } - sleep (1); + +static void +SNACK_print_hex_info (const char*message, + const uint8_t *hexArray, + size_t sizeHex) +{ + char hex[5] = {""}; + char str[64] = {""}; + + for ( unsigned int i = 0; i < sizeHex; ++i ) { + sprintf (hex, "%.2x ", hexArray[i]); + strcat (str, hex); } - return EXIT_SUCCESS; + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "%s: %s\n", message, str); + } +static void +cleanup_payment (struct PaymentActivity *pa) +{ + if (NULL != pa->po) + TALER_MERCHANT_proposal_cancel (pa->po); + if (NULL != pa->ppo) + TALER_MERCHANT_poll_payment_cancel (pa->ppo); + if (NULL != pa->cpo) + TALER_MERCHANT_check_payment_cancel (pa->cpo); + if (NULL != pa->task) + GNUNET_SCHEDULER_cancel (pa->task); + if (NULL != pa->pnd) + { + nfc_initiator_deselect_target (pa->pnd); // needed? + nfc_close (pa->pnd); + } + GNUNET_free_non_null (pa->taler_pay_uri); + GNUNET_free (pa); +} + static void shutdown_task (void *cls) { (void) cls; - /* clear all initialized data */ if (NULL != context) + { nfc_exit (context); - if (NULL != product) - SNACK_product_exit (&product); - if (NULL != curl) - SNACK_taler_exit (&curl); - if (NULL != super_task) - GNUNET_SCHEDULER_cancel (super_task); + context = NULL; + } + if (NULL != payment_activity) + { + cleanup_payment (payment_activity); + payment_activity = NULL; + } + if (NULL != keyboard_task) + { + GNUNET_SCHEDULER_cancel (keyboard_task); + keyboard_task = NULL; + } if (NULL != ctx) { GNUNET_CURL_fini (ctx); @@ -119,101 +204,244 @@ shutdown_task (void *cls) } +/** + * Callback to process a GET /poll-payment request + * + * @param cls closure + * @param http_status HTTP status code for this request + * @param obj raw response body + * @param paid #GNUNET_YES if the payment is settled, #GNUNET_NO if not + * settled, $GNUNET_SYSERR on error + * (note that refunded payments are returned as paid!) + * @param refunded #GNUNET_YES if there is at least on refund on this payment, + * #GNUNET_NO if refunded, #GNUNET_SYSERR or error + * @param refunded_amount amount that was refunded, NULL if there + * was no refund + * @param taler_pay_uri the URI that instructs the wallets to process + * the payment + */ static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) +poll_payment_cb (void *cls, + unsigned int http_status, + const json_t *obj, + int paid, + int refunded, + struct TALER_Amount *refund_amount, + const char *taler_pay_uri) { - /* inizialize product */ - if ( SNACK_product_init (&product, cfg) ) + struct PaymentActivity *pa = cls; + + pa->ppo = NULL; + // FIXME: handle different http_status codes more nicely + if (paid) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "main: unable to init product structure\n"); - global_ret = EXIT_FAILURE; - return; + fprintf (stderr, + "FIXME: yield product here!\n"); + cleanup_payment (pa); + GNUNET_assert (payment_activity == pa); + payment_activity = NULL; } - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - NULL); - /* initialize nfc */ - nfc_init (&context); - if ( ! context ) + else { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "main: unable to init nfc\n"); - global_ret = EXIT_FAILURE; - GNUNET_SCHEDULER_shutdown (); - return; + cleanup_payment (pa); + GNUNET_assert (payment_activity == pa); + payment_activity = NULL; } +} + + +static void +connect_target (void *cls); - /* inizialize taler */ - if ( SNACK_taler_init (&curl) ) + +static void +wallet_select_aid (void *cls); + + +static void +wallet_transmit_uri (void *cls) +{ + struct PaymentActivity *pa = cls; + uint8_t response[] = { 0x00, 0x00 }; + size_t slen = strlen (pa->taler_pay_uri); + uint8_t message[sizeof (put_data) + slen]; + + pa->task = NULL; + memcpy (message, put_data, sizeof (put_data)); + memcpy (&message[sizeof (put_data)], pa->taler_pay_uri, slen); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending 'PUT DATA' command to wallet\n"); + if (nfc_initiator_transceive_bytes (pa->pnd, + message, + sizeof (message), + response, + sizeof(response), + 1 /* timeout, 1 ms */) < 0) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "main: nable to init taler communication\n"); - global_ret = EXIT_FAILURE; - GNUNET_SCHEDULER_shutdown (); + "SNACK_wallet_put_message: Failed to put message\n"); + pa->task = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_UNIT_SECONDS /* FIXME: timeout? */, + &wallet_select_aid, /* TBD: where to resume? */ + pa); return; } - ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, - &is.rc); - rc = GNUNET_CURL_gnunet_rc_create (ctx); - start_read_keyboard (); + pa->task = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_UNIT_SECONDS /* FIXME: timeout? */, + &wallet_transmit_uri, /* TBD: where to resume? */ + pa); } static void -start_read_keyboard () +wallet_select_aid (void *cls) { - struct GNUNET_DISK_FileHandle fh = { FILENO_STDIN }; - - printf ("Waiting for MBD input\n"); - printf ("Enter to simulate Snickers, x to quit\n"); - super_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_ABS, - fh, - &read_product_info, - NULL); + struct PaymentActivity *pa = cls; + uint8_t response[] = { 0x00, 0x00 }; + uint8_t message[sizeof(select_file) + sizeof(taler_aid)]; + + pa->task = NULL; + memcpy (message, select_file, sizeof (select_file)); + memcpy (&message[sizeof (select_file)], taler_aid, sizeof (taler_aid)); + + if (nfc_initiator_transceive_bytes (pa->pnd, + message, + sizeof (message), + response, + sizeof(response), + 1 /* timeout in ms */) < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "nfc_initiator_transceive_bytes: Failed to select apk\n"); + pa->task = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_UNIT_SECONDS /* FIXME: timeout? */, + &wallet_select_aid, /* TBD: where to resume? */ + pa); + return; + } + if (0 == memcmp (response, + APDU_SUCCESS, + sizeof (response))) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Taler wallet found\n"); + pa->task = GNUNET_SCHEDULER_add_now (&wallet_transmit_uri, + pa); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "AID selection failure, return code: %x%x\n", + response[0], + response[1]); + pa->task = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_UNIT_SECONDS /* FIXME: timeout? */, + &wallet_select_aid, /* TBD: where to resume? */ + pa); } + +/* upper and lower bounds for mifare targets uid length */ +#define UID_LEN_UPPER 7 +#define UID_LEN_LOWER 4 + + static void -read_product_info (void *cls) +connect_target (void *cls) { - int input; - - super_task = NULL; - input = getchar (); - if ( (EOF == input) || - ('x' == (char) input) ) + struct PaymentActivity *pa = cls; + const nfc_modulation nmMifare[] = { { + .nmt = NMT_ISO14443A, + .nbr = NBR_106, + } }; + + pa->task = NULL; + pa->nt.nti.nai.szUidLen = 0; + if (nfc_initiator_select_passive_target (pa->pnd, + nmMifare[0], NULL, 0, &pa->nt) <= + 0 ) { - GNUNET_SCHEDULER_shutdown (); - return; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "SNACK_nfc_connect_target: failed to connect\n"); } - switch ((char) input) + else if ( (pa->nt.nti.nai.szUidLen > UID_LEN_UPPER) || + (pa->nt.nti.nai.szUidLen < UID_LEN_LOWER) ) { - case 'c': - if (NULL != payment_activity) - { - cancel_payment (payment_activity); - payment_activity = NULL; - } - break; - case 's': - GNUNET_assert (NULL == payment_activity); - product.amount = "0.1"; - product.product = "Snickers"; - payment_activity = launch_payment (&product); - break; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "SNACK_nfc_connect_target: failed to connect\n"); + SNACK_print_hex_info ("UID", + pa->nt.nti.nai.abtUid, + pa->nt.nti.nai.szUidLen); } - start_read_keyboard (); + else + { + pa->task = GNUNET_SCHEDULER_add_now (&wallet_select_aid, + pa); + return; + } + nfc_initiator_deselect_target (pa->pnd); + pa->task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &connect_target, + pa); } +/** + * Callback to process a GET /check-payment request + * + * @param cls closure + * @param http_status HTTP status code for this request + * @param obj raw response body + * @param paid #GNUNET_YES if the payment is settled, #GNUNET_NO if not + * settled, $GNUNET_SYSERR on error + * (note that refunded payments are returned as paid!) + * @param refunded #GNUNET_YES if there is at least on refund on this payment, + * #GNUNET_NO if refunded, #GNUNET_SYSERR or error + * @param refunded_amount amount that was refunded, NULL if there + * was no refund + * @param taler_pay_uri the URI that instructs the wallets to process + * the payment + */ static void -cancel_payment (struct PaymentActivity *pa) +check_payment_cb (void *cls, + unsigned int http_status, + const json_t *obj, + int paid, + int refunded, + struct TALER_Amount *refund_amount, + const char *taler_pay_uri) { - if (NULL != pa->po) - TALER_MERCHANT_proposal_cancel (pa->po); - GNUNET_free (pa); + struct PaymentActivity *pa = cls; + + pa->cpo = NULL; + // FIXME: check http_status, yada yada + pa->taler_pay_uri = GNUNET_strdup (taler_pay_uri); + pa->pnd = nfc_open (context, NULL); + if (NULL != pa->pnd) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Payment inititation: Unable to open nfc device\n"); + cleanup_payment (pa); + GNUNET_assert (payment_activity == pa); + payment_activity = NULL; + return; + } + if (0 > nfc_initiator_init (pa->pnd)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to initialize NFC device: %s", + nfc_strerror (pa->pnd)); + cleanup_payment (pa); + GNUNET_assert (payment_activity == pa); + payment_activity = NULL; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "NFC in operation %s / %s\n", + nfc_device_get_name (pa->pnd), + nfc_device_get_connstring (pa->pnd)); + pa->task = GNUNET_SCHEDULER_add_now (&connect_target, + pa); } @@ -227,111 +455,260 @@ proposal_cb (void *cls, struct PaymentActivity *pa = cls; pa->po = NULL; - // use(order_id); - // start_nfc_transmission(pa); - // pa->pnd = open(); - // + if (MHD_HTTP_OK != http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to setup order with backend: %u/%d\n", + http_status, + (int) ec); + cleanup_payment (pa); + GNUNET_assert (payment_activity == pa); + payment_activity = NULL; + return; + } + pa->ppo = TALER_MERCHANT_poll_payment (ctx, + backendBaseUrl, + order_id, + NULL /* &FIXME_h_contract! */, + NULL /* snack machine, no Web crap */, + BACKEND_POLL_TIMEOUT, + &poll_payment_cb, + pa); + pa->cpo = TALER_MERCHANT_check_payment (ctx, + backendBaseUrl, + order_id, + NULL /* snack machine, no Web crap */, + &check_payment_cb, + pa); + } static struct PaymentActivity * -launch_payment (const Product *product) +launch_payment (const struct Product *product) { struct PaymentActivity *pa; - + json_t *orderReq; + char *amountStr; + char *fulfillmentUrl; + + /* create the string representing the amount, e.g. "KUDOS:2.5" */ + GNUNET_asprintf (&amountStr, + "%s:%s", + currency, + product->price); + /* create the fulfillment url, e.g. "taler://fulfillment-success/Enjoy+your+ice+cream!"; */ + GNUNET_asprintf (&fulfillmentUrl, + "%s%s%s!", + fulfillmentUrl, + fulfillmentMsg, + product->description); + /* create the json object for the order request */ + orderReq = json_pack ("{ s:s, s:s, s:s }", + SNACK_JSON_REQ_SUMMARY, product->description, + SNACK_JSON_REQ_AMOUNT, amountStr, + SNACK_JSON_REQ_FULFILLMENT, fulfillmentUrl); + GNUNET_free (amountStr); + GNUNET_free (fulfillmentUrl); + if (NULL == orderReq) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "json_pack failed (out of memory?)\n"); + return NULL; + } pa = GNUNET_new (struct PaymentActivity); - pa->product = *product; - // SEE: SNACK_taler_create_order_req (&product); - order = json (); pa->po = TALER_MERCHANT_order_put (ctx, - backend_url, - order, + backendBaseUrl, + orderReq, &proposal_cb, pa); + json_decref (orderReq); + if (NULL == pa->po) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "TALER_MERCHANT_order_put failed (out of memory?)\n"); + GNUNET_free (pa); + return NULL; + } + return pa; } -static void foo () -{ - /* create the order request */ - SNACK_taler_create_order_req (&product); - - /* create the order */ - while ( SNACK_taler_create_order (curl, &product) ) - ; - /* store the order id into the struct */ - SNACK_product_set_order_id (&product); - - /* store the url to check wheter the payment happened or not */ - SNACK_product_set_check_url (&product); +static void +start_read_keyboard (void); - /* check the payment status for the first time */ - while ( SNACK_taler_check_payment_status (curl, &product) ) - ; - /* store the url to pay, to do this task the payment status has to be called before, because the url will be in the response */ - SNACK_product_set_pay_url (&product); +static void +read_keyboard_command (void *cls) +{ + int input; - /* start a thread to send payment request to taler wallet */ - pthread_t nfcThread; - if ( pthread_create (&nfcThread, - NULL, - &start_nfc_transmission, - NULL) ) + keyboard_task = NULL; + input = getchar (); + if ( (EOF == input) || + ('x' == (char) input) ) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "main: thread creation failed\n"); - return EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; } - - /* check the payment status, if paid exit while loop and end thread transmitting nfc messages */ - while ( ! product.paid ) + if ((char) input == 'c') { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "main: order payment processing\n"); - sleep (5); - - while ( SNACK_taler_check_payment_status (curl, &product) ) - ; - - /* set the boolean paid member of ProductOrder struct */ - SNACK_product_set_paid_status (&product); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "main: payment status paid: %s\n", - (product.paid ? "true" : "false") ); + if (NULL != payment_activity) + { + cleanup_payment (payment_activity); + payment_activity = NULL; + } + else + { + fprintf (stderr, + "No purchase activity pending\n"); + } + start_read_keyboard (); + return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "main: order no.: %s paid!\n", - product.orderID); + if (NULL == payment_activity) + { + fprintf (stderr, + "Purchase activity already pending\n"); + start_read_keyboard (); + return; + } + for (unsigned int i = 0; NULL != products[i].price; i++) + if (((char) input) == products[i].key) + { + payment_activity = launch_payment (&products[i]); + start_read_keyboard (); + return; + } + fprintf (stderr, + "Unknown command `%c'\n", + (char) input); + start_read_keyboard (); +} - /* ----------------- - Here comes the code for releasing the product - ----------------- */ +static void +start_read_keyboard () +{ + struct GNUNET_DISK_FileHandle fh = { STDIN_FILENO }; + + GNUNET_assert (NULL == keyboard_task); + printf ("c' to cancel last purchase, 'x' to quit\n"); + for (unsigned int i = 0; NULL != products[i].price; i++) + printf ("'%c' to buy %s\n", + products[i].key, + products[i].description); + printf ("Waiting for keyboard input\n"); + keyboard_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + &fh, + &read_keyboard_command, + NULL); +} - /* send cancel request to nfc thread */ - while ( pthread_cancel (nfcThread) != 0 ) +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + (void) cls; + (void) args; + (void) cfgfile; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler", + "currency", + &currency)) { - printf ("Error sending cancel request to thread for nfc transmission"); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler", + "currency"); + global_ret = EXIT_FAILURE; + return; } - - void*res; - if ( pthread_join (nfcThread, &res) == 0 ) + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler-mdb", + "backend-base-url", + &backendBaseUrl)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler-mdb", + "backend-base-url"); + global_ret = EXIT_FAILURE; + return; + } + { + char *auth; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler-mdb", + "backend-authorization", + &auth)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler-mdb", + "backend-authorization"); + global_ret = EXIT_FAILURE; + return; + } + GNUNET_asprintf (&authorization, + "%s: %s", + SNACK_CURL_AUTH_HEADER, + auth); + GNUNET_free (auth); + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler-mdb", + "fulfillment-url", + &fulfillmentUrl)) { - printf ("Thread for nfc transmission finished\n"); - fflush (stdout); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler-mdb", + "fulfillment-url"); + global_ret = EXIT_FAILURE; + return; } - else if ( res == PTHREAD_CANCELED ) + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler-mdb", + "fulfillment-msg", + &fulfillmentMsg)) { - printf ("Thread for nfc transmission finished\n"); - fflush (stdout); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler-mdb", + "fulfillment-msgcurrency"); + global_ret = EXIT_FAILURE; + return; } - /* reset the product */ - SNACK_product_reset (&product); + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); + /* initialize nfc */ + nfc_init (&context); + if (NULL == context) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to initialize nfc (nfc_init() failed)\n"); + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + /* initialize HTTP client */ + ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &rc); + rc = GNUNET_CURL_gnunet_rc_create (ctx); + /* setup authorization */ + GNUNET_assert (GNUNET_OK == + GNUNET_CURL_append_header (ctx, + authorization)); + start_read_keyboard (); } - int main (int argc, char*const*argv) @@ -345,7 +722,7 @@ main (int argc, GNUNET_PROGRAM_run (argc, argv, "taler-mdb", - "This is an application for snack machines to pay with GNU Taler via nfc.\n" + "This is an application for snack machines to pay with GNU Taler via nfc.\n", options, &run, NULL)) diff --git a/src/nfc.c b/src/nfc.c @@ -30,103 +30,3 @@ along with #include "nfc.h" #include "wallet.h" - - -/* upper and lower bounds for mifare targets uid length */ -#define UID_LEN_UPPER 7 -#define UID_LEN_LOWER 4 - - -int SNACK_nfc_transmit (nfc_context *context, const char *talerPayUrl, size_t - urlSize) -{ - nfc_device *pnd; - nfc_target nt; - - pnd = nfc_open (context, NULL); - if ( ! pnd ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_nfc_transmit: Unable to open nfc device\n"); - return EXIT_FAILURE; - } - - /* initialize device as reader */ - if ( nfc_initiator_init (pnd) < 0 ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Snack_nfc_transmit: nfc_initator_init error: %s", - nfc_strerror (pnd) ); - nfc_close (pnd); - return EXIT_FAILURE; - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "SNACK_nfc_transmit: device: %s\n", - nfc_device_get_name (pnd)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "SNACK_nfc_transmit: connstring: %s\n", - nfc_device_get_connstring (pnd)); - - /* connect to a target device */ - if ( SNACK_nfc_connect_target (pnd, &nt) ) - { - nfc_close (pnd); - return EXIT_FAILURE; - } - - /* send the message to the wallet */ - if ( SNACK_wallet_transmit (pnd, talerPayUrl, urlSize) ) - { - /* the transmition failed, the target has to be reselected when using MIFARE as defined in libnfc --> exit */ - nfc_close (pnd); - return EXIT_FAILURE; - } - - /* clean up */ - nfc_initiator_deselect_target (pnd); - nfc_close (pnd); - - return EXIT_SUCCESS; -} - -int SNACK_nfc_connect_target (nfc_device *pnd, nfc_target *nt) -{ - /* ctr for how many tries to connect a target */ - int ctr = 2; - - const nfc_modulation nmMifare[] = { { - .nmt = NMT_ISO14443A, - .nbr = NBR_106, - } }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "SNACK_nfc_connect_target: trying to connect to target\n"); - - while ( ctr > 0 ) - { - /* set uid length to zero ( in case of second selecting the length still has the old value ) */ - nt->nti.nai.szUidLen = 0; - if ( nfc_initiator_select_passive_target (pnd, nmMifare[0], NULL, 0, nt) <= - 0 ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_nfc_connect_target: failed to connect\n"); - } - else if ((nt->nti.nai.szUidLen > UID_LEN_UPPER) ||(nt->nti.nai.szUidLen < - UID_LEN_LOWER) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_nfc_connect_target: failed to connect\n"); - SNACK_print_hex_info ("UID", nt->nti.nai.abtUid, nt->nti.nai.szUidLen); - } - else { - /*GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "SNACK_nfc_connect_target: Target selected!\n"); - SNACK_print_hex_info ("UID", nt->nti.nai.abtUid, nt->nti.nai.szUidLen);*/ - return EXIT_SUCCESS; - } - sleep (1); - ctr--; - } - - return EXIT_FAILURE; -} diff --git a/src/product.c b/src/product.c @@ -32,95 +32,7 @@ the attributes. #include "configuration.h" #include "communication.h" -void SNACK_product_reset (ProductOrder *product) -{ - product->amount = NULL; - product->product = NULL; - product->paid = false; - - /* regenerate nonce */ - SNACK_product_generate_nonce (&(product->nonce), NONCE_SIZE); -} - -int SNACK_product_init (ProductOrder *product, struct - GNUNET_CONFIGURATION_Handle *cfg) -{ - if ( ! product ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_product_init: product order struct must be provided\n"); - return EXIT_FAILURE; - } - - /* allocate the taler config struct */ - product->talerCfg = GNUNET_malloc (sizeof(TalerConfig)); - /* get the configurable parameters and store them in the productOrder struct */ - if ( SNACK_product_set_cfg_value (cfg, TALER, CURRENCY, - &product->talerCfg->currency)) - return EXIT_FAILURE; - if ( SNACK_product_set_cfg_value (cfg, BACKOFFICE, BACKEND_BASE_URL, - &product->talerCfg->backendBaseUrl)) - return EXIT_FAILURE; - if ( SNACK_product_set_cfg_value (cfg, TALER, FULLFILLMENT_URL, - &product->talerCfg->fulfillmentUrl)) - return EXIT_FAILURE; - if ( SNACK_product_set_cfg_value (cfg, TALER, FULLFILLMENT_MSG, - &product->talerCfg->fulfillmentMsg)) - return EXIT_FAILURE; - - - /* create the Authorization: ..... option for curl */ - char*auth; - if ( SNACK_product_set_cfg_value (cfg, BACKOFFICE, AUTHORIZATION, &auth)) - return EXIT_FAILURE; - product->talerCfg->authorization = GNUNET_malloc (strlen ( - SNACK_CURL_AUTH_HEADER) - + strlen (": ") + strlen ( - auth) + 1); - sprintf (product->talerCfg->authorization, "%s: %s", SNACK_CURL_AUTH_HEADER, - auth); - GNUNET_free (auth); - - - /* malloc for every string member with size 1, every other func just calls - * realloc, because this struct gets used over and over again */ - product->response = GNUNET_malloc (sizeof(TalerResponse)); - product->response->string = GNUNET_malloc (1); - product->orderID = GNUNET_malloc (1); - product->payUrl = GNUNET_malloc (1); - product->checkUrl = GNUNET_malloc (1); - product->orderRequest = GNUNET_malloc (1); - product->amount = NULL; - product->product = NULL; - product->paid = false; - - /* generate nonce */ - product->nonce = GNUNET_malloc (NONCE_SIZE); - SNACK_product_generate_nonce (&(product->nonce), NONCE_SIZE); - - return EXIT_SUCCESS; -} - -void SNACK_product_exit (ProductOrder *product) -{ - GNUNET_free (product->response->string); - GNUNET_free (product->response); - GNUNET_free (product->nonce); - GNUNET_free (product->orderID); - GNUNET_free (product->orderRequest); - GNUNET_free (product->payUrl); - GNUNET_free (product->checkUrl); - GNUNET_free (product->talerCfg->currency); - GNUNET_free (product->talerCfg->authorization); - GNUNET_free (product->talerCfg->backendBaseUrl); - GNUNET_free (product->talerCfg->fulfillmentUrl); - GNUNET_free (product->talerCfg->fulfillmentMsg); - GNUNET_free (product->talerCfg); - product->amount = NULL; - product->product = NULL; - product->paid = false; -} int SNACK_product_set_check_url (ProductOrder *product) { @@ -138,11 +50,6 @@ int SNACK_product_set_check_url (ProductOrder *product) return EXIT_SUCCESS; } -int SNACK_product_set_order_id (ProductOrder *product) -{ - return SNACK_taler_parse_json (product->response, SNACK_JSON_ORDER_ID, - &(product->orderID)); -} int SNACK_product_set_pay_url (ProductOrder *product) { @@ -192,32 +99,3 @@ int SNACK_product_set_paid_status (ProductOrder *product) return EXIT_SUCCESS; } - - -int SNACK_product_set_cfg_value (struct GNUNET_CONFIGURATION_Handle *cfg, const - char*section, const char *key, char **value) -{ - - if ( GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, section, key, - value)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_product_set_cfg_value: no value '%s' or section '%s' in config file found\n", - key, section); - return EXIT_FAILURE; - } - return EXIT_SUCCESS; -} - -void SNACK_product_generate_nonce (char**nonce, size_t size) -{ - uint32_t temp; - for ( size_t i = 0; i < size; ++i) { - do temp = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 122); - while ( (temp < '1' || temp > '9') & (temp < 'A' || temp > 'Z') & (temp < - 'a' || - temp > - 'z') ); - (*nonce)[i] = temp; - } -} diff --git a/src/product.h b/src/product.h @@ -40,15 +40,6 @@ typedef struct TalerResponse size_t size; } TalerResponse; -typedef struct TalerConfig -{ - char *currency; - char *backendBaseUrl; - char *fulfillmentUrl; - char *fulfillmentMsg; - char *authorization; -} TalerConfig; - typedef struct ProductOrder { char *product; @@ -59,7 +50,6 @@ typedef struct ProductOrder char *payUrl; char *orderRequest; bool paid; - TalerConfig *talerCfg; TalerResponse *response; } ProductOrder; diff --git a/src/wallet.c b/src/wallet.c @@ -36,52 +36,10 @@ along with int SNACK_wallet_select_aid (nfc_device *pnd) { - uint8_t response[] = { 0x00, 0x00 }; - - int size = sizeof(select_file) + sizeof(taler_aid); - uint8_t message[size]; - if ( SNACK_concat_message (select_file, sizeof(select_file), taler_aid, - message, - size) ) - return EXIT_FAILURE; - - /* - SNACK_print_hex_info ( - "SNACK_wallet_select_aid: Selecting Taler apk using AID", taler_aid, - sizeof(taler_aid)); - */ - - if ( nfc_initiator_transceive_bytes (pnd, message, size, response, - sizeof(response), TRANSMIT_TIMEOUT) < 0 ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_wallet_select_aid: Failed to select apk\n"); - return EXIT_FAILURE; - } - - return SNACK_check_response (response, sizeof(response) ); } int SNACK_put_message (nfc_device *pnd, const char *msg, size_t msgSize) { - uint8_t response[] = { 0x00, 0x00 }; - int size = sizeof(put_data) + msgSize; - uint8_t message[size]; - - if ( SNACK_concat_message (put_data, sizeof(put_data), (const uint8_t *) msg, - message, size) ) - return EXIT_FAILURE; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "SNACK_wallet_put_messsage: Sending 'PUT DATA' command to wallet\n"); - - if ( nfc_initiator_transceive_bytes (pnd, message, size, response, - sizeof(response), TRANSMIT_TIMEOUT) < 0 ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_wallet_put_message: Failed to put message\n"); - return EXIT_FAILURE; - } return SNACK_check_response (response, sizeof(response) ); } @@ -110,32 +68,6 @@ int SNACK_wallet_tunneling (nfc_device *pnd, const char *msg, size_t msgSize) } -int SNACK_wallet_transmit (nfc_device *pnd, const char *msg, size_t msgLen) -{ - if ( ! msg ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "SNACK_wallet_transmit: No message to send\n"); - return EXIT_FAILURE; - } - - if ( SNACK_wallet_select_aid (pnd) ) - return EXIT_FAILURE; - if ( SNACK_put_message (pnd, msg, msgLen) ) - return EXIT_FAILURE; - - // ------------------------------------------------------------------------------------------- - // sleep(3); - // while ( SNACK_wallet_tunneling(pnd, msg, msgLen) == EXIT_SUCCESS ) - // sleep(3); - // ; - // -------------------------------------------------------------------------------------------- - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "SNACK_wallet_transmit: Transmitted message to taler wallet\n"); - - return EXIT_SUCCESS; -} int SNACK_check_response (uint8_t *response, uint8_t responseLen) { @@ -173,17 +105,3 @@ int SNACK_concat_message (const uint8_t*command, size_t commandSize, const return EXIT_SUCCESS; } - -void SNACK_print_hex_info (const char*message, const uint8_t *hexArray, size_t - sizeHex) -{ - char hex[5] = {""}; - char str[64] = {""}; - - for ( unsigned int i = 0; i < sizeHex; ++i ) { - sprintf (hex, "%.2x ", hexArray[i]); - strcat (str, hex); - } - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "%s: %s\n", message, str); - -}