summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README3
-rw-r--r--configure.ac83
-rw-r--r--m4/libcurl.m4251
-rw-r--r--src/Makefile.am2
-rw-r--r--src/backend-lib/Makefile.am27
-rw-r--r--src/backend-lib/merchant_api_contract.c60
-rw-r--r--src/backend-lib/merchant_api_deposit.c111
-rw-r--r--src/backend-lib/merchant_db.c662
-rw-r--r--src/backend-lib/merchant_db.h198
-rw-r--r--src/backend-lib/taler_merchant_contract_lib.h11
-rw-r--r--src/backend-lib/taler_merchant_deposit_lib.h28
-rw-r--r--src/backend-lib/taler_merchant_lib.h2
-rw-r--r--src/backend/Makefile.am30
-rw-r--r--src/backend/QUESTIONS6
-rw-r--r--src/backend/README7
-rw-r--r--src/backend/merchant.c219
-rw-r--r--src/backend/merchant.conf43
-rw-r--r--src/backend/taler-merchant-httpd.c421
-rw-r--r--src/backend/taler-merchant-httpd.h132
-rw-r--r--src/backend/taler-merchant-httpd_auditors.c239
-rw-r--r--src/backend/taler-merchant-httpd_auditors.h74
-rw-r--r--src/backend/taler-merchant-httpd_contract.c147
-rw-r--r--src/backend/taler-merchant-httpd_contract.h9
-rw-r--r--src/backend/taler-merchant-httpd_mhd.c (renamed from src/backend/taler-mint-httpd_mhd.c)14
-rw-r--r--src/backend/taler-merchant-httpd_mhd.h (renamed from src/backend/taler-mint-httpd_mhd.h)4
-rw-r--r--src/backend/taler-merchant-httpd_mints.c530
-rw-r--r--src/backend/taler-merchant-httpd_mints.h105
-rw-r--r--src/backend/taler-merchant-httpd_parsing.c (renamed from src/backend/taler-mint-httpd_parsing.c)15
-rw-r--r--src/backend/taler-merchant-httpd_parsing.h (renamed from src/backend/taler-mint-httpd_parsing.h)0
-rw-r--r--src/backend/taler-merchant-httpd_pay.c902
-rw-r--r--src/backend/taler-merchant-httpd_pay.h10
-rw-r--r--src/backend/taler-merchant-httpd_responses.c (renamed from src/backend/taler-mint-httpd_responses.c)111
-rw-r--r--src/backend/taler-merchant-httpd_responses.h (renamed from src/backend/taler-mint-httpd_responses.h)52
-rw-r--r--src/backend/taler-mint-httpd.h85
-rw-r--r--src/backenddb/Makefile.am39
-rw-r--r--src/backenddb/merchantdb_plugin.c150
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c244
-rw-r--r--src/frontend/fullfillment.php25
-rw-r--r--src/include/Makefile.am9
-rw-r--r--src/include/merchant.h204
-rw-r--r--src/include/platform.h4
-rw-r--r--src/include/taler_merchant_service.h292
-rw-r--r--src/include/taler_merchantdb_lib.h54
-rw-r--r--src/include/taler_merchantdb_plugin.h88
-rw-r--r--src/lib/Makefile.am47
-rw-r--r--src/lib/merchant_api_context.c525
-rw-r--r--src/lib/merchant_api_context.h169
-rw-r--r--src/lib/merchant_api_json.c491
-rw-r--r--src/lib/merchant_api_json.h331
-rw-r--r--src/lib/merchant_api_pay.c372
-rw-r--r--src/lib/test_merchant_api.c1345
-rw-r--r--src/merchant/.gitignore1
-rw-r--r--src/merchant/Makefile.am55
-rw-r--r--src/merchant/merchant.c173
-rw-r--r--src/merchant/merchant.h110
-rw-r--r--src/merchant/merchant_db.c347
-rw-r--r--src/merchant/merchant_db.h98
-rw-r--r--src/merchant/taler_merchant_dbinit.c79
-rw-r--r--src/merchant/taler_merchant_serve.c1539
-rw-r--r--src/merchant/test_merchant.c96
-rw-r--r--src/merchant/test_merchant.conf36
-rw-r--r--src/merchant/test_merchant.ecc2
-rw-r--r--src/merchant/test_merchant_db.c147
-rw-r--r--src/tests/test_contract.c10
64 files changed, 6531 insertions, 5144 deletions
diff --git a/README b/README
index e1480831..a5a4873f 100644
--- a/README
+++ b/README
@@ -1,6 +1,3 @@
-sources in src/merchant/ are obsolete, therefore the configure system does not
-provide support for building them.
-
The new merchant's layout is reflected by the directories 'frontend' and 'backend',
being respectively the PHP website homepage and the C 'core' which is charge of
providing all the cryptographic and DB related primitives.
diff --git a/configure.ac b/configure.ac
index 67d84f11..67d5450c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,9 +2,9 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
-AC_INIT([taler], [0.0.0], [taler-bug@gnunet.org])
-AC_CONFIG_SRCDIR([src/merchant/merchant.c])
-AC_CONFIG_HEADERS([taler_config.h])
+AC_INIT([taler-merchant], [0.0.0], [taler-bug@gnunet.org])
+AC_CONFIG_SRCDIR([src/backend/taler-merchant-httpd.c])
+AC_CONFIG_HEADERS([taler_merchant_config.h])
# support for non-recursive builds
AM_INIT_AUTOMAKE([subdir-objects])
@@ -50,14 +50,12 @@ AS_IF([test $libgnunetutil != 1],
*** ]])])
-# check for libpq (postgresql)
+# test for postgres
AX_LIB_POSTGRESQL([9.3])
-AS_IF([test ! "$found_postgresql" = "yes"],
- [AC_MSG_ERROR([[
-***
-*** You need postgresql / libpq to build this program.
-*** ]])])
-
+if test "$found_postgresql" = "yes"; then
+ postgres=true
+fi
+AM_CONDITIONAL(HAVE_POSTGRESQL, test x$postgres = xtrue)
# Check for Taler's libtalerpq
libtalerpq=0
@@ -81,13 +79,7 @@ AC_CHECK_HEADERS([taler/taler_pq_lib.h],
[], [#ifdef HAVE_GNUNET_PLATFORM_H
#include <gnunet/platform.h>
#endif])
-AS_IF([test $libtalerpq != 1],
- [AC_MSG_ERROR([[
-***
-*** You need libtalerpq to build this program.
-*** This library is part of the Taler MINT, available at
-*** https://taler.net
-*** ]])])
+AM_CONDITIONAL(HAVE_TALERPQ, test x$libtalerpq = x1)
# check for libmicrohttpd
@@ -134,7 +126,8 @@ AS_IF([test $jansson = 0],
*** ]])])
# check for libgnurl
-LIBGNURL_CHECK_CONFIG([], [7.34.0], [gnurl=1], [gnurl=0])
+# libgnurl
+LIBGNURL_CHECK_CONFIG(,7.34.0,gnurl=1,gnurl=0)
if test "$gnurl" = 1
then
AM_CONDITIONAL(HAVE_LIBGNURL, true)
@@ -142,11 +135,52 @@ then
else
AM_CONDITIONAL(HAVE_LIBGNURL, false)
fi
-AS_IF([test $gnurl = 0],
- [AC_MSG_ERROR([[
-***
-*** You need libgnurl to build this program.
-*** ]])])
+
+# libcurl-gnutls
+LIBCURL_CHECK_CONFIG(,7.34.0,[curl=true],[curl=false])
+if test "x$curl" = xtrue
+then
+ AC_CHECK_HEADERS([curl/curl.h],
+ AC_CHECK_DECLS(CURLINFO_TLS_SESSION,[curl=true],[curl=false],[[#include <curl/curl.h>]]),
+ [curl=false])
+ # need libcurl-gnutls.so, everything else is not acceptable
+ AC_CHECK_LIB([curl-gnutls],[curl_easy_getinfo],,[curl=false])
+ # cURL must support CURLINFO_TLS_SESSION, version >= 7.34
+
+fi
+if test x$curl = xfalse
+then
+ AM_CONDITIONAL(HAVE_LIBCURL, false)
+if test "$gnurl" = 0
+then
+ AC_MSG_WARN([GNUnet requires libcurl-gnutls >= 7.34])
+fi
+else
+ AM_CONDITIONAL(HAVE_LIBCURL, true)
+ AC_DEFINE([HAVE_LIBCURL],[1],[Have CURL])
+fi
+
+# libgnurl
+if test "x$gnurl" = "x0"
+then
+ if test "x$curl" = "x0"
+ then
+ AC_MSG_NOTICE([NOTICE: libgnurl not found. http client support will not be compiled.])
+ AC_MSG_WARN([ERROR: libgnurl not found. hostlist daemon will not be compiled, and you probably WANT the hostlist daemon])
+ else
+ AC_MSG_NOTICE([WARNING: libgnurl not found, trying to use libcurl-gnutls instead.])
+ fi
+fi
+
+# gcov compilation
+AC_MSG_CHECKING(whether to compile with support for code coverage analysis)
+AC_ARG_ENABLE([coverage],
+ AS_HELP_STRING([--enable-coverage],
+ [compile the library with code coverage support]),
+ [use_gcov=${enableval}],
+ [use_gcov=no])
+AC_MSG_RESULT($use_gcov)
+AM_CONDITIONAL([USE_COVERAGE], [test "x$use_gcov" = "xyes"])
# Require minimum libgcrypt version
need_libgcrypt_version=1.6.1
@@ -180,6 +214,7 @@ AC_CHECK_FUNCS([strdup])
AC_CONFIG_FILES([Makefile
src/Makefile
src/include/Makefile
+src/backenddb/Makefile
src/backend/Makefile
-src/backend-lib/Makefile])
+src/lib/Makefile])
AC_OUTPUT
diff --git a/m4/libcurl.m4 b/m4/libcurl.m4
new file mode 100644
index 00000000..a84077a5
--- /dev/null
+++ b/m4/libcurl.m4
@@ -0,0 +1,251 @@
+# LIBCURL_CHECK_CONFIG ([DEFAULT-ACTION], [MINIMUM-VERSION],
+# [ACTION-IF-YES], [ACTION-IF-NO])
+# ----------------------------------------------------------
+# David Shaw <dshaw@jabberwocky.com> May-09-2006
+#
+# Checks for libcurl. DEFAULT-ACTION is the string yes or no to
+# specify whether to default to --with-libcurl or --without-libcurl.
+# If not supplied, DEFAULT-ACTION is yes. MINIMUM-VERSION is the
+# minimum version of libcurl to accept. Pass the version as a regular
+# version number like 7.10.1. If not supplied, any version is
+# accepted. ACTION-IF-YES is a list of shell commands to run if
+# libcurl was successfully found and passed the various tests.
+# ACTION-IF-NO is a list of shell commands that are run otherwise.
+# Note that using --without-libcurl does run ACTION-IF-NO.
+#
+# This macro #defines HAVE_LIBCURL if a working libcurl setup is
+# found, and sets @LIBCURL@ and @LIBCURL_CPPFLAGS@ to the necessary
+# values. Other useful defines are LIBCURL_FEATURE_xxx where xxx are
+# the various features supported by libcurl, and LIBCURL_PROTOCOL_yyy
+# where yyy are the various protocols supported by libcurl. Both xxx
+# and yyy are capitalized. See the list of AH_TEMPLATEs at the top of
+# the macro for the complete list of possible defines. Shell
+# variables $libcurl_feature_xxx and $libcurl_protocol_yyy are also
+# defined to 'yes' for those features and protocols that were found.
+# Note that xxx and yyy keep the same capitalization as in the
+# curl-config list (e.g. it's "HTTP" and not "http").
+#
+# Users may override the detected values by doing something like:
+# LIBCURL="-lcurl" LIBCURL_CPPFLAGS="-I/usr/myinclude" ./configure
+#
+# For the sake of sanity, this macro assumes that any libcurl that is
+# found is after version 7.7.2, the first version that included the
+# curl-config script. Note that it is very important for people
+# packaging binary versions of libcurl to include this script!
+# Without curl-config, we can only guess what protocols are available,
+# or use curl_version_info to figure it out at runtime.
+
+AC_DEFUN([LIBCURL_CHECK_CONFIG],
+[
+ AH_TEMPLATE([LIBCURL_FEATURE_SSL],[Defined if libcurl supports SSL])
+ AH_TEMPLATE([LIBCURL_FEATURE_KRB4],[Defined if libcurl supports KRB4])
+ AH_TEMPLATE([LIBCURL_FEATURE_IPV6],[Defined if libcurl supports IPv6])
+ AH_TEMPLATE([LIBCURL_FEATURE_LIBZ],[Defined if libcurl supports libz])
+ AH_TEMPLATE([LIBCURL_FEATURE_ASYNCHDNS],[Defined if libcurl supports AsynchDNS])
+ AH_TEMPLATE([LIBCURL_FEATURE_IDN],[Defined if libcurl supports IDN])
+ AH_TEMPLATE([LIBCURL_FEATURE_SSPI],[Defined if libcurl supports SSPI])
+ AH_TEMPLATE([LIBCURL_FEATURE_NTLM],[Defined if libcurl supports NTLM])
+
+ AH_TEMPLATE([LIBCURL_PROTOCOL_HTTP],[Defined if libcurl supports HTTP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_HTTPS],[Defined if libcurl supports HTTPS])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_FTP],[Defined if libcurl supports FTP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_FTPS],[Defined if libcurl supports FTPS])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_FILE],[Defined if libcurl supports FILE])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_TELNET],[Defined if libcurl supports TELNET])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_POP3],[Defined if libcurl supports POP3])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_IMAP],[Defined if libcurl supports IMAP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_SMTP],[Defined if libcurl supports SMTP])
+
+ AC_ARG_WITH(libcurl,
+ AC_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]),
+ [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])])
+
+ if test "$_libcurl_with" != "no" ; then
+
+ AC_PROG_AWK
+
+ _libcurl_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'"
+
+ _libcurl_try_link=yes
+
+ if test -d "$_libcurl_with" ; then
+ LIBCURL_CPPFLAGS="-I$withval/include"
+ _libcurl_ldflags="-L$withval/lib"
+ AC_PATH_PROG([_libcurl_config],[curl-config],[],
+ ["$withval/bin"])
+ else
+ AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH])
+ fi
+
+ if test x$_libcurl_config != "x" ; then
+ AC_CACHE_CHECK([for the version of libcurl],
+ [libcurl_cv_lib_curl_version],
+ [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`])
+
+ _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse`
+ _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse`
+
+ if test $_libcurl_wanted -gt 0 ; then
+ AC_CACHE_CHECK([for libcurl >= version $2],
+ [libcurl_cv_lib_version_ok],
+ [
+ if test $_libcurl_version -ge $_libcurl_wanted ; then
+ libcurl_cv_lib_version_ok=yes
+ else
+ libcurl_cv_lib_version_ok=no
+ fi
+ ])
+ fi
+
+ if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then
+ if test x"$LIBCURL_CPPFLAGS" = "x" ; then
+ LIBCURL_CPPFLAGS=`$_libcurl_config --cflags`
+ fi
+ if test x"$LIBCURL" = "x" ; then
+ LIBCURL=`$_libcurl_config --libs`
+
+ # This is so silly, but Apple actually has a bug in their
+ # curl-config script. Fixed in Tiger, but there are still
+ # lots of Panther installs around.
+ case "${host}" in
+ powerpc-apple-darwin7*)
+ LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'`
+ ;;
+ esac
+ fi
+
+ # All curl-config scripts support --feature
+ _libcurl_features=`$_libcurl_config --feature`
+
+ # Is it modern enough to have --protocols? (7.12.4)
+ if test $_libcurl_version -ge 461828 ; then
+ _libcurl_protocols=`$_libcurl_config --protocols`
+ fi
+ else
+ _libcurl_try_link=no
+ fi
+
+ unset _libcurl_wanted
+ fi
+
+ if test $_libcurl_try_link = yes ; then
+
+ # we didn't find curl-config, so let's see if the user-supplied
+ # link line (or failing that, "-lcurl") is enough.
+ LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"}
+
+ AC_CACHE_CHECK([whether libcurl is usable],
+ [libcurl_cv_lib_curl_usable],
+ [
+ _libcurl_save_cppflags=$CPPFLAGS
+ CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS"
+ _libcurl_save_libs=$LIBS
+ LIBS="$LIBCURL $LIBS"
+
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <curl/curl.h>]],[[
+/* Try and use a few common options to force a failure if we are
+ missing symbols or can't link. */
+int x;
+curl_easy_setopt(NULL,CURLOPT_URL,NULL);
+x=CURL_ERROR_SIZE;
+x=CURLOPT_WRITEFUNCTION;
+x=CURLOPT_WRITEDATA;
+x=CURLOPT_ERRORBUFFER;
+x=CURLOPT_STDERR;
+x=CURLOPT_VERBOSE;
+if (x) ;
+]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no)
+
+ CPPFLAGS=$_libcurl_save_cppflags
+ LIBS=$_libcurl_save_libs
+ unset _libcurl_save_cppflags
+ unset _libcurl_save_libs
+ ])
+
+ if test $libcurl_cv_lib_curl_usable = yes ; then
+
+ # Does curl_free() exist in this version of libcurl?
+ # If not, fake it with free()
+
+ _libcurl_save_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS"
+ _libcurl_save_libs=$LIBS
+ LIBS="$LIBS $LIBCURL"
+
+ AC_CHECK_FUNC(curl_free,,
+ AC_DEFINE(curl_free,free,
+ [Define curl_free() as free() if our version of curl lacks curl_free.]))
+
+ CPPFLAGS=$_libcurl_save_cppflags
+ LIBS=$_libcurl_save_libs
+ unset _libcurl_save_cppflags
+ unset _libcurl_save_libs
+
+ AC_DEFINE(HAVE_LIBCURL,1,
+ [Define to 1 if you have a functional curl library.])
+ AC_SUBST(LIBCURL_CPPFLAGS)
+ AC_SUBST(LIBCURL)
+
+ for _libcurl_feature in $_libcurl_features ; do
+ AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1])
+ eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes
+ done
+
+ if test "x$_libcurl_protocols" = "x" ; then
+
+ # We don't have --protocols, so just assume that all
+ # protocols are available
+ _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
+
+ if test x$libcurl_feature_SSL = xyes ; then
+ _libcurl_protocols="$_libcurl_protocols HTTPS"
+
+ # FTPS wasn't standards-compliant until version
+ # 7.11.0 (0x070b00 == 461568)
+ if test $_libcurl_version -ge 461568; then
+ _libcurl_protocols="$_libcurl_protocols FTPS"
+ fi
+ fi
+
+ # RTSP, IMAP, POP3 and SMTP were added in
+ # 7.20.0 (0x071400 == 463872)
+ if test $_libcurl_version -ge 463872; then
+ _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
+ fi
+ fi
+
+ for _libcurl_protocol in $_libcurl_protocols ; do
+ AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1])
+ eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes
+ done
+ else
+ unset LIBCURL
+ unset LIBCURL_CPPFLAGS
+ fi
+ fi
+
+ unset _libcurl_try_link
+ unset _libcurl_version_parse
+ unset _libcurl_config
+ unset _libcurl_feature
+ unset _libcurl_features
+ unset _libcurl_protocol
+ unset _libcurl_protocols
+ unset _libcurl_version
+ unset _libcurl_ldflags
+ fi
+
+ if test x$_libcurl_with = xno || test x$libcurl_cv_lib_curl_usable != xyes ; then
+ # This is the IF-NO path
+ ifelse([$4],,:,[$4])
+ else
+ # This is the IF-YES path
+ ifelse([$3],,:,[$3])
+ fi
+
+ unset _libcurl_with
+])dnl
diff --git a/src/Makefile.am b/src/Makefile.am
index 665be7d3..4d26b2c8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,2 +1,2 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/include
-SUBDIRS = include backend-lib backend
+SUBDIRS = include backenddb backend
diff --git a/src/backend-lib/Makefile.am b/src/backend-lib/Makefile.am
deleted file mode 100644
index 583c4d03..00000000
--- a/src/backend-lib/Makefile.am
+++ /dev/null
@@ -1,27 +0,0 @@
-# This Makefile.am is in the public domain
-AM_CPPFLAGS = -I$(top_srcdir)/src/include
-
-lib_LTLIBRARIES = \
- libtalermerchant.la
-
-include_HEADERS = \
- taler_merchant_lib.h \
- taler_merchant_contract_lib.h \
- taler_merchant_deposit_lib.h
-
-libtalermerchant_la_SOURCES = \
- merchant_api_contract.c \
- merchant_api_deposit.c \
- taler_merchant_contract_lib.h \
- taler_merchant_deposit_lib.h \
- merchant_db.c merchant_db.h \
- merchant.h
-
-libtalermerchant_la_LIBADD = \
- $(LIBGCRYPT_LIBS) \
- -ltalerutil \
- -lgnunetutil \
- -ltalerpq \
- -lgnunetpostgres \
- -lpq \
- -lpthread
diff --git a/src/backend-lib/merchant_api_contract.c b/src/backend-lib/merchant_api_contract.c
deleted file mode 100644
index 20b69cd3..00000000
--- a/src/backend-lib/merchant_api_contract.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant_db.c
- * @brief DB work related to contract management
- * @author Marcello Stanisci
- */
-
-#include "platform.h"
-#include <jansson.h>
-#include <taler/taler_signatures.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "merchant.h"
-#include "merchant_db.h"
-#include "taler_merchant_contract_lib.h"
-
-/**
- * Take the global wire details and return a JSON containing them,
- * compliantly with the Taler's API.
- * @param wire the merchant's wire details
- * @param salt the nounce for hashing the wire details with
- * @param edate when the beneficiary wants this transfer to take place
- * @return JSON representation of the wire details, NULL upon errors
- */
-
-json_t *
-MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire,
- uint64_t salt)
-
-{
-
- json_t *root;
- json_t *j_salt;
-
- j_salt = json_integer (salt);
-
- if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:I}",
- "type", "SEPA",
- "IBAN", wire->iban,
- "name", wire->name,
- "bic", wire->bic,
- "r", json_integer_value (j_salt))))
- return NULL;
-
- return root;
-}
diff --git a/src/backend-lib/merchant_api_deposit.c b/src/backend-lib/merchant_api_deposit.c
deleted file mode 100644
index a3cae20c..00000000
--- a/src/backend-lib/merchant_api_deposit.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant_db.c
- * @brief DB and crypto work related to deposit management
- * @author Marcello Stanisci
- */
-
-
-#include "platform.h"
-#include <jansson.h>
-#include <taler/taler_signatures.h>
-#include <gnunet/gnunet_util_lib.h>
-#include <taler/taler_util.h>
-#include "merchant.h"
-#include "merchant_db.h"
-#include "taler_merchant_contract_lib.h"
-
-/**
-* Verify the signature on a successful deposit permission
-* @param h_contract the hashed stringification of this contract
-* @param h_wire the hashed 'wire' object holdign the merchant bank's details
-* @param timestamp the 32bit wide number representing the number of seconds
-* since the Epoch
-* @param refund the refund deadline for this deal, expressed in seconds as @a
-* timestamp
-* @param trans_id an id number for this deal
-* @param amount_minus_fee what paid minus its deposit fee
-* @param coin_pub the coin's public key
-* @param sig the mint's signature
-* @param mint_pub mint's key to verify this signature against
-* @return GNUNET_OK if the verification succeeds, GNUNET_NO if not,
-* GNUNET_SYSERR upon errors
-*/
-
-uint32_t
-MERCHANT_verify_confirmation (const struct GNUNET_HashCode *h_contract,
- const struct GNUNET_HashCode *h_wire,
- struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute refund,
- uint64_t trans_id,
- const struct TALER_Amount *amount_minus_fee,
- const struct TALER_CoinSpendPublicKeyP *coin,
- const struct TALER_MerchantPublicKeyP *merchant,
- const struct GNUNET_CRYPTO_EddsaSignature *sig,
- const struct TALER_MintPublicKeyP *mint_pub)
-{
- struct TALER_DepositConfirmationPS dc;
-
- dc.h_contract = *h_contract;
- dc.h_wire = *h_wire;
-
- dc.merchant = *merchant;
- dc.coin_pub = *coin;
-
- dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
- dc.refund_deadline = GNUNET_TIME_absolute_hton (refund);
- TALER_amount_hton (&dc.amount_without_fee, amount_minus_fee);
- dc.transaction_id = GNUNET_htonll (trans_id);
-
- #ifdef DEBUG
- char *hwire_enc;
- char *hcontract_enc;
- char *merchant_enc;
- char *coinpub_enc;
-
- hwire_enc = GNUNET_STRINGS_data_to_string_alloc (h_wire, sizeof (struct GNUNET_HashCode));
- hcontract_enc = GNUNET_STRINGS_data_to_string_alloc (h_contract, sizeof (struct GNUNET_HashCode));
- merchant_enc = GNUNET_STRINGS_data_to_string_alloc (&merchant.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
- coinpub_enc = GNUNET_STRINGS_data_to_string_alloc (&coin.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
-
- printf ("Signing Confirmation:\nH_wire: %s\nH_contract: %s\nmerchant_pub: %s\ncoin_pub: %s\n"
- "timestamp: %llu,\nrefund: %llu,\namount: %s %llu.%lu,\ntrid: %llu\n",
- hwire_enc,
- hcontract_enc,
- merchant_enc,
- coinpub_enc,
- timestamp_abs.abs_value_us,
- refund_abs.abs_value_us,
- amount_minus_fee->currency,
- amount_minus_fee->value,
- amount_minus_fee->fraction,
- trans_id);
- #endif
-
- dc.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT);
- dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
-
- if (GNUNET_SYSERR ==
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT,
- &dc.purpose,
- sig,
- &mint_pub->eddsa_pub))
- return GNUNET_NO;
- return GNUNET_OK;
-}
-
diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c
deleted file mode 100644
index f30e3d88..00000000
--- a/src/backend-lib/merchant_db.c
+++ /dev/null
@@ -1,662 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant_db.c
- * @brief database helper functions used by the merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <taler/taler_util.h>
-#include <taler/taler_pq_lib.h>
-#include "merchant_db.h"
-
-
-#define PQSQL_strerror(kind, cmd, res) \
- GNUNET_log_from (kind, "merchant-db", \
- "SQL %s failed at %s:%u with error: %s", \
- cmd, __FILE__, __LINE__, PQresultErrorMessage (res));
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Connect to postgresql database
- *
- * @param cfg the configuration handle
- * @return connection to the postgresql database; NULL upon error
- */
-PGconn *
-MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- return GNUNET_POSTGRES_connect (cfg, "merchant-db");
-}
-
-
-/**
- * Disconnect from the database
- *
- * @param conn database handle to close
- */
-void
-MERCHANT_DB_disconnect (PGconn *conn)
-{
- PQfinish (conn);
-}
-
-
-/**
- * Initialize merchant tables
- *
- * @param conn the connection handle to postgres db.
- * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
- * contents are dropped when the @a conn is closed
- * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
- */
-int
-MERCHANT_DB_initialize (PGconn *conn, int tmp)
-{
- const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
- char *sql;
- PGresult *res;
- ExecStatusType status;
- int ret;
-
- res = NULL;
- (void) GNUNET_asprintf (&sql,
- "BEGIN TRANSACTION;"
- "CREATE %1$s TABLE IF NOT EXISTS contracts ("
- "contract_id INT8 PRIMARY KEY,"
- "hash BYTEA NOT NULL,"
- "amount INT8 NOT NULL,"
- "amount_fraction INT4 NOT NULL,"
- "amount_currency VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL,"
- "description TEXT NOT NULL,"
- "nounce INT8 NOT NULL,"
- "timestamp INT8 NOT NULL,"
- "expiry INT8 NOT NULL,"
- "edate INT8 NOT NULL,"
- "refund_deadline INT8 NOT NULL,"
- "product INT8 NOT NULL);"
- "CREATE %1$s TABLE IF NOT EXISTS checkouts ("
- "coin_pub BYTEA PRIMARY KEY,"
- "contract_id INT8 REFERENCES contracts(contract_id),"
- "amount INT4 NOT NULL,"
- "amount_fraction INT4 NOT NULL,"
- "coin_sig BYTEA NOT NULL);"
- "CREATE %1$s TABLE IF NOT EXISTS deposits ("
- "dep_perm VARCHAR NOT NULL,"
- "transaction_id INT8,"
- "pending INT4 NOT NULL,"
- "mint_url VARCHAR NOT NULL);",
- tmp_str);
- ret = GNUNET_POSTGRES_exec (conn, sql);
- (void) GNUNET_POSTGRES_exec (conn,
- (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK;");
- GNUNET_free (sql);
- if (GNUNET_OK != ret)
- return ret;
-
- while (NULL != (res = PQgetResult (conn)))
- {
- status = PQresultStatus (res);
- PQclear (res);
- }
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "contract_create",
- "INSERT INTO contracts"
- "(contract_id, hash, timestamp, expiry, edate,"
- "refund_deadline, amount, amount_fraction, amount_currency,"
- "description, nounce, product) VALUES"
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
- 12, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
-
- /* Query aimed to get the contract's nounce and edate which will be
- both used for regenerating a 'wire' JSON object to insert into the
- deposit permission. Implicitly, this query will tell whether a contract
- was created or not */
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "get_contract_hash",
- "SELECT "
- "nounce, edate "
- "FROM contracts "
- "WHERE ("
- "hash=$1"
- ")",
- 1, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "get_contract_set",
- "SELECT "
- "contract_id, nounce, timestamp, edate, "
- "refund_deadline FROM contracts "
- "WHERE ("
- "hash=$1"
- ")",
- 1, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "store_deposit_permission",
- "INSERT INTO deposits"
- "(dep_perm, transaction_id, pending, mint_url) "
- "VALUES ($1, $2, $3, $4);", 4, NULL)));
-
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "update_deposit_permission",
- "UPDATE deposits "
- "SET pending = $1 "
- "WHERE transaction_id = $2", 2, NULL)));
-
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "get_contract_product",
- "SELECT ("
- "product"
- ") FROM contracts "
- "WHERE ("
- "contract_id=$1"
- ")",
- 1, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "checkout_create",
- "INSERT INTO checkouts ("
- "coin_pub,"
- "contract_id,"
- "amount,"
- "amount_fraction,"
- "coin_sig"
- ") VALUES ("
- "$1, $2, $3, $4, $5"
- ")",
- 5, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "get_checkout_product",
- "SELECT ("
- "product"
- ") FROM contracts "
- "WHERE "
- "contract_id IN ("
- "SELECT (contract_id) FROM checkouts "
- "WHERE coin_pub=$1"
- ")",
- 1, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
- PQclear (res);
-
- return GNUNET_OK;
-
- EXITIF_exit:
- if (NULL != res)
- {
- PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res);
- PQclear (res);
- }
- return GNUNET_SYSERR;
-}
-
-/**
- * Update the pending column of a deposit permission
- * @param conn handle to DB
- * @param transaction_id identification number of the deposit to
- * update
- * @param pending true if still pending, false otherwise (i.e. the
- * mint did respond something)
- * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
- */
-uint32_t
-MERCHANT_DB_update_deposit_permission (PGconn *conn,
- uint64_t transaction_id,
- unsigned int pending)
-{
- PGresult *res;
- ExecStatusType status;
-
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_uint32 (&pending),
- TALER_PQ_query_param_uint64 (&transaction_id),
- TALER_PQ_query_param_end
- };
-
- res = TALER_PQ_exec_prepared (conn, "update_deposit_permission", params);
- status = PQresultStatus (res);
-
- if (PGRES_COMMAND_OK != status)
- {
-
- const char *sqlstate;
-
- sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE);
- if (NULL == sqlstate)
- {
- /* very unexpected... */
- GNUNET_break (0);
- PQclear (res);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Database commit failure: %s\n",
- sqlstate);
- PQclear (res);
- return GNUNET_SYSERR;
- }
-}
-
-/**
- * Store a deposit permission in DB. To be mainly used if /deposit should
- * be retried; also, the merchant can benefit from this information in case
- * he needs to later investigate about some transaction_id.
- * @param conn DB handle
- * @param transaction_id identification number of this payment (which is the
- * same id of the related contract)
- * @param pending if true, this payment got to a persistent state
- * @param which mint is to get this deposit permission
- * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
- */
-uint32_t
-MERCHANT_DB_store_deposit_permission (PGconn *conn,
- const char *deposit_permission,
- uint64_t transaction_id,
- unsigned int pending,
- const char *mint_url)
-{
- PGresult *res;
- ExecStatusType status;
-
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_fixed_size (deposit_permission, strlen (deposit_permission)),
- TALER_PQ_query_param_uint64 (&transaction_id),
- TALER_PQ_query_param_uint32 (&pending),
- TALER_PQ_query_param_fixed_size (mint_url, strlen (mint_url)),
- TALER_PQ_query_param_end
- };
- res = TALER_PQ_exec_prepared (conn, "store_deposit_permission", params);
- status = PQresultStatus (res);
-
- if (PGRES_COMMAND_OK != status)
- {
- const char *sqlstate;
-
- sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE);
- if (NULL == sqlstate)
- {
- /* very unexpected... */
- GNUNET_break (0);
- PQclear (res);
- return GNUNET_SYSERR;
- }
- /* 40P01: deadlock, 40001: serialization failure */
- if ( (0 == strcmp (sqlstate,
- "23505")))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Inserting same transaction id twice\n");
- /* Primary key violation */
- PQclear (res);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Database commit failure: %s\n",
- sqlstate);
- PQclear (res);
- return GNUNET_SYSERR;
- }
-
- PQclear (res);
- return GNUNET_OK;
-}
-
-/**
-* Insert a contract record into the database and if successfull
-* return the serial number of the inserted row.
-*
-* @param conn the database connection
-* @param timestamp the timestamp of this contract
-* @param expiry the time when the contract will expire
-* @param edate when the merchant wants to receive the wire transfer
-* corresponding to this deal (this value is also a field inside the
-* 'wire' JSON format)
-* @param refund deadline until which the merchant can return the paid
-* amount
-* @param amount the taler amount corresponding to the contract
-* @param hash of the stringified JSON corresponding to this contract
-* @param c_id contract's id
-* @param desc descripition of the contract
-* @param nounce a random 64-bit nounce
-* @param product description to identify a product
-* @return GNUNET_OK on success, GNUNET_NO if attempting to insert an
-* already inserted @a c_id, GNUNET_SYSERR for other errors.
-*/
-
-uint32_t
-MERCHANT_DB_contract_create (PGconn *conn,
- const struct GNUNET_TIME_Absolute timestamp,
- const struct GNUNET_TIME_Absolute expiry,
- struct GNUNET_TIME_Absolute edate,
- struct GNUNET_TIME_Absolute refund,
- const struct TALER_Amount *amount,
- const struct GNUNET_HashCode *h_contract,
- uint64_t c_id,
- const char *desc,
- uint64_t nounce,
- uint64_t product)
-{
- PGresult *res;
- #if 0
- uint64_t expiry_ms_nbo;
- uint64_t value_nbo;
- uint32_t fraction_nbo;
- uint64_t nounce_nbo;
- #endif
- ExecStatusType status;
-
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_uint64 (&c_id),
- TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)),
- TALER_PQ_query_param_absolute_time (&timestamp),
- TALER_PQ_query_param_absolute_time (&expiry),
- TALER_PQ_query_param_absolute_time (&edate),
- TALER_PQ_query_param_absolute_time (&refund),
- TALER_PQ_query_param_amount (amount),
- /* A *string* is being put in the following statement,
- though the column is declared as *blob*. Will this be
- liked by the DB ? */
- TALER_PQ_query_param_fixed_size (desc, strlen (desc)),
- TALER_PQ_query_param_uint64 (&nounce),
- TALER_PQ_query_param_uint64 (&product),
- TALER_PQ_query_param_end
- };
-
- /* NOTE: the statement is prepared by MERCHANT_DB_initialize function */
- res = TALER_PQ_exec_prepared (conn, "contract_create", params);
- status = PQresultStatus (res);
-
- if (PGRES_COMMAND_OK != status)
- {
- const char *sqlstate;
-
- sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE);
- if (NULL == sqlstate)
- {
- /* very unexpected... */
- GNUNET_break (0);
- PQclear (res);
- return GNUNET_SYSERR;
- }
- /* 40P01: deadlock, 40001: serialization failure */
- if ( (0 == strcmp (sqlstate,
- "23505")))
- {
- /* Primary key violation */
- PQclear (res);
- return GNUNET_NO;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Database commit failure: %s\n",
- sqlstate);
- PQclear (res);
- return GNUNET_SYSERR;
- }
-
- PQclear (res);
- return GNUNET_OK;
-
-}
-
-long long
-MERCHANT_DB_get_contract_product (PGconn *conn,
- uint64_t contract_id)
-{
- PGresult *res;
- uint64_t product;
- ExecStatusType status;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_uint64 (&contract_id),
- TALER_PQ_query_param_end
- };
- struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_uint64 ("product", &product),
- TALER_PQ_result_spec_end
- };
-
- contract_id = GNUNET_htonll (contract_id);
- res = TALER_PQ_exec_prepared (conn, "get_contract_product", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
- PQclear (res);
- return GNUNET_ntohll ((uint64_t) product);
-
- EXITIF_exit:
- PQclear (res);
- return -1;
-}
-
-unsigned int
-MERCHANT_DB_checkout_create (PGconn *conn,
- struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub,
- uint64_t transaction_id,
- struct TALER_Amount *amount,
- struct GNUNET_CRYPTO_rsa_Signature *coin_sig)
-{
- PGresult *res;
- ExecStatusType status;
- uint32_t value_nbo;
- uint32_t fraction_nbo;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_rsa_public_key (coin_pub),
- TALER_PQ_query_param_uint64 (&transaction_id),
- TALER_PQ_query_param_uint32 (&value_nbo),
- TALER_PQ_query_param_uint32 (&fraction_nbo),
- TALER_PQ_query_param_rsa_signature (coin_sig),
- TALER_PQ_query_param_end
- };
-
- transaction_id = GNUNET_htonll (transaction_id);
- value_nbo = htonl (amount->value);
- fraction_nbo = htonl (amount->fraction);
- res = TALER_PQ_exec_prepared (conn, "checkout_create", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_COMMAND_OK != status);
- PQclear (res);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (res);
- return GNUNET_SYSERR;
-}
-
-
-long long
-MERCHANT_DB_get_checkout_product (PGconn *conn,
- struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub)
-{
- PGresult *res;
- ExecStatusType status;
- uint64_t product;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_rsa_public_key (coin_pub),
- TALER_PQ_query_param_end
- };
- struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_uint64 ("product", &product),
- TALER_PQ_result_spec_end
- };
-
- product = -1;
- res = TALER_PQ_exec_prepared (conn, "get_checkout_product", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- if (0 == PQntuples (res))
- {
- TALER_LOG_DEBUG ("Checkout not found for given coin");
- goto EXITIF_exit;
- }
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
- PQclear (res);
- return GNUNET_ntohll ((uint64_t) product);
-
- EXITIF_exit:
- PQclear (res);
- return -1;
-}
-/* end of merchant-db.c */
-
-
-/**
-* The query gets a contract's nounce and edate used to reproduce
-* a 'wire' JSON object. This function is also useful to check whether
-* a claimed contract existed or not.
-* @param conn handle to the DB
-* @param h_contract the parameter for the row to match against
-* @param nounce where to store the found nounce
-* @param edate where to store the found edate
-* @return GNUNET_OK on success, GNUNET_SYSERR upon errors
-*
-*/
-
-uint32_t
-MERCHANT_DB_get_contract_values (PGconn *conn,
- const struct GNUNET_HashCode *h_contract,
- uint64_t *nounce,
- struct GNUNET_TIME_Absolute *edate)
-{
- PGresult *res;
- ExecStatusType status;
-
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)),
- TALER_PQ_query_param_end
- };
-
- struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_uint64 ("nounce", nounce),
- TALER_PQ_result_spec_absolute_time ("edate", edate),
- TALER_PQ_result_spec_end
- };
-
- res = TALER_PQ_exec_prepared (conn, "get_contract_hash", params);
-
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- if (0 == PQntuples (res))
- {
- TALER_LOG_DEBUG ("Contract not found");
- goto EXITIF_exit;
- }
-
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
- PQclear (res);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (res);
- return GNUNET_SYSERR;
-}
-
-/**
-* Get a set of values representing a contract. This function is meant
-* to obsolete the '_get_contract_values' version.
-* @param h_contract the hashcode of this contract
-* @param contract_handle where to store the results
-* @raturn GNUNET_OK in case of success, GNUNET_SYSERR
-* upon errors
-*
-*/
-
-uint32_t
-MERCHANT_DB_get_contract_handle (PGconn *conn,
- const struct GNUNET_HashCode *h_contract,
- struct MERCHANT_contract_handle *contract_handle)
-{
- struct MERCHANT_contract_handle ch;
- PGresult *res;
- ExecStatusType status;
-
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)),
- TALER_PQ_query_param_end
- };
-
- struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_uint64 ("nounce", &ch.nounce),
- TALER_PQ_result_spec_absolute_time ("edate", &ch.edate),
- TALER_PQ_result_spec_absolute_time ("timestamp", &ch.timestamp),
- TALER_PQ_result_spec_absolute_time ("refund_deadline", &ch.refund_deadline),
- TALER_PQ_result_spec_uint64 ("contract_id", &ch.contract_id),
- TALER_PQ_result_spec_end
- };
-
- res = TALER_PQ_exec_prepared (conn, "get_contract_set", params);
-
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- if (0 == PQntuples (res))
- {
- TALER_LOG_DEBUG ("Contract not found");
- goto EXITIF_exit;
- }
-
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
- *contract_handle = ch;
- PQclear (res);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (res);
- return GNUNET_SYSERR;
-}
diff --git a/src/backend-lib/merchant_db.h b/src/backend-lib/merchant_db.h
deleted file mode 100644
index aa157712..00000000
--- a/src/backend-lib/merchant_db.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant_db.h
- * @brief database helper functions used by the merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#ifndef MERCHANT_DB_H
-#define MERCHANT_DB_H
-
-#include <gnunet/gnunet_postgres_lib.h>
-#include <taler/taler_util.h>
-
-/* Set of values that represent a contract. To be expanded on an
- as-needed basis */
-struct MERCHANT_contract_handle
-{
- /* The nounce used when hashing the wire details
- for this contract */
- uint64_t nounce;
-
- /* The maximum time when the merchant expects the money tranfer
- to his bank account to happen */
- struct GNUNET_TIME_Absolute edate;
-
- /* The time when this contract was generated */
- struct GNUNET_TIME_Absolute timestamp;
-
- /* The maximum time until which the merchant could issue a
- refund to the customer */
- struct GNUNET_TIME_Absolute refund_deadline;
-
- /* The identification number for this contract */
- uint64_t contract_id;
-
-};
-
-/**
- * Connect to postgresql database
- *
- * @param cfg the configuration handle
- * @return connection to the postgresql database; NULL upon error
- */
-PGconn *
-MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-
-/**
- * Disconnect from the database
- *
- * @param conn database handle to close
- */
-void
-MERCHANT_DB_disconnect (PGconn *conn);
-
-
-/**
- * Initialize merchant tables
- *
- * @param conn the connection handle to postgres db.
- * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
- * contents are dropped when the @a conn is closed
- * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
- */
-int
-MERCHANT_DB_initialize (PGconn *conn, int tmp);
-
-
-/**
-* Inserts a contract record into the database and if successfull returns the
-* serial number of the inserted row.
-*
-* @param conn the database connection
-* @param timestamp the timestamp of this contract
-* @param expiry the time when the contract will expire
-* @param edate when the merchant wants to receive the wire transfer corresponding
-* to this deal (this value is also a field inside the 'wire' JSON format)
-* @param refund deadline until which the merchant can return the paid amount
-* @param amount the taler amount corresponding to the contract
-* @param hash of the stringified JSON corresponding to this contract
-* @param c_id contract's id
-* @param desc descripition of the contract
-* @param nounce a random 64-bit nounce
-* @param product description to identify a product
-* @return GNUNET_OK on success, GNUNET_SYSERR upon error
-*/
-
-uint32_t
-MERCHANT_DB_contract_create (PGconn *conn,
- const struct GNUNET_TIME_Absolute timestamp,
- const struct GNUNET_TIME_Absolute expiry,
- struct GNUNET_TIME_Absolute edate,
- struct GNUNET_TIME_Absolute refund,
- const struct TALER_Amount *amount,
- const struct GNUNET_HashCode *h_contract,
- uint64_t c_id,
- const char *desc,
- uint64_t nounce,
- uint64_t product);
-
-long long
-MERCHANT_DB_get_contract_product (PGconn *conn,
- uint64_t contract_id);
-
-/**
- * Update the pending column of a deposit permission
- * @param conn handle to DB
- * @param transaction_id identification number of the deposit to
- * update
- * @param pending true if still pending, false otherwise (i.e. the
- * mint did respond something)
- * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
- */
-uint32_t
-MERCHANT_DB_update_deposit_permission (PGconn *conn,
- uint64_t transaction_id,
- unsigned int pending);
-
-unsigned int
-MERCHANT_DB_checkout_create (PGconn *conn,
- struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub,
- uint64_t transaction_id,
- struct TALER_Amount *amount,
- struct GNUNET_CRYPTO_rsa_Signature *coin_sig);
-
-
-long long
-MERCHANT_DB_get_checkout_product (PGconn *conn,
- struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub);
-
-/**
-* The query gets a contract's nounce and edate used to reproduce
-* a 'wire' JSON object. This function is also useful to check whether
-* a claimed contract existed or not.
-* @param conn handle to the DB
-* @param h_contract the parameter for the row to match against
-* @param nounce where to store the found nounce
-* @param edate where to store the found edate
-* @return GNUNET_OK on success, GNUNET_SYSERR upon errors
-*
-*/
-
-uint32_t
-MERCHANT_DB_get_contract_values (PGconn *conn,
- const struct GNUNET_HashCode *h_contract,
- uint64_t *nounce,
- struct GNUNET_TIME_Absolute *edate);
-
-#endif /* MERCHANT_DB_H */
-
-/**
-* Get a set of values representing a contract. This function is meant
-* to obsolete the '_get_contract_values' version.
-* @param h_contract the hashcode of this contract
-* @param contract_handle where to store the results
-* @raturn GNUNET_OK in case of success, GNUNET_SYSERR
-* upon errors
-*
-*/
-
-uint32_t
-MERCHANT_DB_get_contract_handle (PGconn *conn,
- const struct GNUNET_HashCode *h_contract,
- struct MERCHANT_contract_handle *contract_handle);
-
-/**
- * Store a deposit permission in DB. To be mainly used if /deposit should
- * be retried; also, the merchant can benefit from this information in case
- * he needs to later investigate about some transaction_id.
- * @param conn DB handle
- * @param transaction_id identification number of this payment (which is the
- * same id of the related contract)
- * @param pending if true, this payment got to a persistent state
- * @param which mint is to get this deposit permission
- * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
- */
-uint32_t
-MERCHANT_DB_store_deposit_permission (PGconn *conn,
- const char *deposit_permission,
- uint64_t transaction_id,
- unsigned int pending,
- const char *mint_url);
-/* end of merchant-db.h */
diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h
deleted file mode 100644
index 3f95841f..00000000
--- a/src/backend-lib/taler_merchant_contract_lib.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Take the global wire details and return a JSON containing them,
- * compliantly with the Taler's API.
- * @param wire the merchant's wire details
- * @param salt the nounce for hashing the wire details with
- * @return JSON representation of the wire details, NULL upon errors
- */
-
-json_t *
-MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire,
- uint64_t salt);
diff --git a/src/backend-lib/taler_merchant_deposit_lib.h b/src/backend-lib/taler_merchant_deposit_lib.h
deleted file mode 100644
index 95bbea78..00000000
--- a/src/backend-lib/taler_merchant_deposit_lib.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
-* Verify the signature on a successful deposit permission
-* @param h_contract the hashed stringification of this contract
-* @param h_wire the hashed 'wire' object holdign the merchant bank's details
-* @param timestamp the 32bit wide number representing the number of seconds
-* since the Epoch
-* @param refund the refund deadline for this deal, expressed in seconds as @a
-* timestamp
-* @param trans_id an id number for this deal
-* @param amount_minus_fee what paid minus its deposit fee
-* @param coin_pub the coin's public key
-* @param sig the mint's signature
-* @param mint_pub mint's key to verify this signature against
-* @return GNUNET_OK if the verification succeeds, GNUNET_NO if not,
-* GNUNET_SYSERR upon errors
-*/
-
-uint32_t
-MERCHANT_verify_confirmation (const struct GNUNET_HashCode *h_contract,
- const struct GNUNET_HashCode *h_wire,
- struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute refund,
- uint64_t trans_id,
- const struct TALER_Amount *amount_minus_fee,
- const struct TALER_CoinSpendPublicKeyP *coin,
- const struct TALER_MerchantPublicKeyP *merchant,
- const struct GNUNET_CRYPTO_EddsaSignature *sig,
- const struct TALER_MintPublicKeyP *mint_pub);
diff --git a/src/backend-lib/taler_merchant_lib.h b/src/backend-lib/taler_merchant_lib.h
deleted file mode 100644
index ff8d85b7..00000000
--- a/src/backend-lib/taler_merchant_lib.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "taler_merchant_contract_lib.h"
-#include "taler_merchant_deposit_lib.h"
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 08601781..a99fab99 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -6,28 +6,18 @@ bin_PROGRAMS = \
taler_merchant_httpd_SOURCES = \
taler-merchant-httpd.c taler-merchant-httpd.h \
- merchant.c merchant.h \
- ../backend-lib/merchant_db.c ../backend-lib/merchant_db.h \
- taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \
- taler-mint-httpd_responses.c taler-mint-httpd_responses.h \
- taler-mint-httpd.h \
- taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \
- taler-merchant-httpd_contract.c \
- taler-merchant-httpd_contract.h \
- taler-merchant-httpd_pay.c \
- taler-merchant-httpd_pay.h
-
+ taler-merchant-httpd_parsing.c taler-merchant-httpd_parsing.h \
+ taler-merchant-httpd_responses.c taler-merchant-httpd_responses.h \
+ taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
+ taler-merchant-httpd_auditors.c taler-merchant-httpd_auditors.h \
+ taler-merchant-httpd_mints.c taler-merchant-httpd_mints.h \
+ taler-merchant-httpd_contract.c taler-merchant-httpd_contract.h \
+ taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h
taler_merchant_httpd_LDADD = \
- $(LIBGCRYPT_LIBS) \
+ $(top_srcdir)/src/backenddb/libtalermerchantdb.la \
+ -ltalermint \
-ltalerutil \
-lmicrohttpd \
-ljansson \
- -lcurl \
- -lgnunetutil \
- $(top_srcdir)/src/backend-lib/libtalermerchant.la \
- -ltalermint \
- -ltalerpq \
- -lgnunetpostgres \
- -lpq \
- -lpthread
+ -lgnunetutil
diff --git a/src/backend/QUESTIONS b/src/backend/QUESTIONS
deleted file mode 100644
index b992e96f..00000000
--- a/src/backend/QUESTIONS
+++ /dev/null
@@ -1,6 +0,0 @@
-1. why does the merchant daemon appears to be some executable under some
-.libs directory even though it gets launched through an executable located
-elsewhere?
-
-2. why does the httpd prints three times the URL corresponding to GET /some/url
-?
diff --git a/src/backend/README b/src/backend/README
deleted file mode 100644
index 999dd9e6..00000000
--- a/src/backend/README
+++ /dev/null
@@ -1,7 +0,0 @@
-Here are the files implementing the backend which is in charge of doing
-cryptographic calls, binary manipulations and some HTTP/JSON communication.
-
-NOTE:
-
-Makefile.am contains some hardcoded paths that need to be tuned to pick
-the right files.
diff --git a/src/backend/merchant.c b/src/backend/merchant.c
deleted file mode 100644
index 02b37fb8..00000000
--- a/src/backend/merchant.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant.c
- * @brief Common utility functions for merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include "merchant.h"
-
-
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Parses mints from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of mints upon successful parsing. Will be NULL upon
- * error.
- * @return the number of mints in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_Mint **mints)
-{
- char *mints_str;
- char *token_nf; /* do no free (nf) */
- char *mint_section;
- char *mint_hostname;
- struct MERCHANT_Mint *r_mints;
- struct MERCHANT_Mint mint;
- unsigned int cnt;
- int OK;
-
- OK = 0;
- mints_str = NULL;
- token_nf = NULL;
- mint_section = NULL;
- mint_hostname = NULL;
- r_mints = NULL;
- cnt = 0;
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "merchant",
- "TRUSTED_MINTS",
- &mints_str));
- for (token_nf = strtok (mints_str, " ");
- NULL != token_nf;
- token_nf = strtok (NULL, " "))
- {
- GNUNET_assert (0 < GNUNET_asprintf (&mint_section,
- "mint-%s", token_nf));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- mint_section,
- "HOSTNAME",
- &mint_hostname));
- mint.hostname = mint_hostname;
- GNUNET_array_append (r_mints, cnt, mint);
- mint_hostname = NULL;
- GNUNET_free (mint_section);
- mint_section = NULL;
- }
- OK = 1;
-
- EXITIF_exit:
- GNUNET_free_non_null (mints_str);
- GNUNET_free_non_null (mint_section);
- GNUNET_free_non_null (mint_hostname);
- if (!OK)
- {
- GNUNET_free_non_null (r_mints);
- return GNUNET_SYSERR;
- }
-
- *mints = r_mints;
- return cnt;
-}
-
-/**
- * Parses auditors from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of auditors upon successful parsing. Will be NULL upon
- * error.
- * @return the number of auditors in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_auditors (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_Auditor **auditors)
-{
- char *auditors_str;
- char *token_nf; /* do no free (nf) */
- char *auditor_section;
- char *auditor_name;
- struct MERCHANT_Auditor *r_auditors;
- struct MERCHANT_Auditor auditor;
- unsigned int cnt;
- int OK;
-
- OK = 0;
- auditors_str = NULL;
- token_nf = NULL;
- auditor_section = NULL;
- auditor_name = NULL;
- r_auditors = NULL;
- cnt = 0;
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "merchant",
- "AUDITORS",
- &auditors_str));
- for (token_nf = strtok (auditors_str, " ");
- NULL != token_nf;
- token_nf = strtok (NULL, " "))
- {
- GNUNET_assert (0 < GNUNET_asprintf (&auditor_section,
- "auditor-%s", token_nf));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- auditor_section,
- "NAME",
- &auditor_name));
- auditor.name = auditor_name;
- GNUNET_array_append (r_auditors, cnt, auditor);
- auditor_name = NULL;
- GNUNET_free (auditor_section);
- auditor_section = NULL;
- }
- OK = 1;
-
- EXITIF_exit:
- GNUNET_free_non_null (auditors_str);
- GNUNET_free_non_null (auditor_section);
- GNUNET_free_non_null (auditor_name);
- if (!OK)
- {
- GNUNET_free_non_null (r_auditors);
- return GNUNET_SYSERR;
- }
-
- *auditors = r_auditors;
- return cnt;
-}
-
-
-/**
- * Parse the SEPA information from the configuration. If any of the required
- * fileds is missing return NULL.
- *
- * @param cfg the configuration
- * @return Sepa details as a structure; NULL upon error
- */
-struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- struct MERCHANT_WIREFORMAT_Sepa *wf;
-
- wf = GNUNET_new (struct MERCHANT_WIREFORMAT_Sepa);
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "IBAN",
- &wf->iban));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "NAME",
- &wf->name));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "BIC",
- &wf->bic));
- return wf;
-
- EXITIF_exit:
- GNUNET_free_non_null (wf->iban);
- GNUNET_free_non_null (wf->name);
- GNUNET_free_non_null (wf->bic);
- GNUNET_free (wf);
- return NULL;
-
-}
-
-
-/**
- * Destroy and free resouces occupied by the wireformat structure
- *
- * @param wf the wireformat structure
- */
-void
-TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf)
-{
- GNUNET_free_non_null (wf->iban);
- GNUNET_free_non_null (wf->name);
- GNUNET_free_non_null (wf->bic);
- GNUNET_free (wf);
-}
-
-/* end of merchant.c */
diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf
index 4515fd41..80a7b412 100644
--- a/src/backend/merchant.conf
+++ b/src/backend/merchant.conf
@@ -1,21 +1,52 @@
+# Sample configuration file for a merchant.
[merchant]
+
+# Which port do we run the backend on? (HTTP server)
PORT = 9966
+
+# FIXME: is this one used?
HOSTNAME = localhost
-TRUSTED_MINTS = taler
+
+# Where is our private key?
KEYFILE = merchant.priv
+
+# What currency does this backend accept?
CURRENCY = KUDOS
+
+# FIXME: to be revised
+TRUSTED_MINTS = taler
+
+# How quickly do we want the mint to send us our money?
+# Used only if the frontend does not specify a value.
+# FIXME: EDATE is a bit short, 'execution_delay'?
EDATE = 3 week
-AUDITORS = france
+
+# Which plugin (backend) do we use for the DB.
+DB = postgres
[mint-taler]
-HOSTNAME = mint.demo.taler.net
+URI = mint.demo.taler.net
+MASTER_KEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0
+
+# Auditors must be in sections "auditor-", the rest of the section
+# name could be anything.
+[auditor-ezb]
+# Informal name of the auditor. Just for the user.
+NAME = European Central Bank
+
+# URI of the auditor (especially for in the future, when the
+# auditor offers an automated issue reporting system).
+# Not really used today.
+URI = http://taler.ezb.eu/
-[auditor-france]
-NAME = Charles De Gaulle
+# This is the important bit: the signing key of the auditor.
+PUBLIC_KEY = 9QXF7XY7E9VPV47B5Z806NDFSX2VJ79SVHHD29QEQ3BG31ANHZ60
-[merchant-db]
+# This specifies which database we use.
+[merchantdb-postgres]
CONFIG = postgres:///talerdemo
+# "wire-" sections include wire details, here for SEPA.
[wire-sepa]
IBAN = DE67830654080004822650
NAME = GNUNET E.V
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index ba359bd1..2cc553e9 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -13,40 +13,40 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
-
/**
* @file merchant/backend/taler-merchant-httpd.c
* @brief HTTP serving layer intended to perform crypto-work and
* communication with the mint
* @author Marcello Stanisci
+ * @author Christian Grothoff
*/
-
#include "platform.h"
#include <microhttpd.h>
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
-#include <curl/curl.h>
#include <taler/taler_util.h>
#include <taler/taler_mint_service.h>
-#include "taler-mint-httpd_parsing.h"
-#include "taler-mint-httpd_responses.h"
-#include "merchant_db.h"
-#include "merchant.h"
-#include "taler_merchant_lib.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_responses.h"
+#include "taler_merchantdb_lib.h"
#include "taler-merchant-httpd.h"
-#include "taler-mint-httpd_mhd.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_auditors.h"
+#include "taler-merchant-httpd_mints.h"
#include "taler-merchant-httpd_contract.h"
#include "taler-merchant-httpd_pay.h"
+
+
/**
- * Our hostname
+ * Our wire format details in JSON format (with salt).
*/
-static char *hostname;
+struct json_t *j_wire;
/**
- * The port we are running on
+ * Hash of our wire format details as given in #j_wire.
*/
-static long long unsigned port;
+struct GNUNET_HashCode h_wire;
/**
* Merchant's private key
@@ -54,30 +54,35 @@ static long long unsigned port;
struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
/**
- * File holding the merchant's private key
+ * Merchant's public key
*/
-char *keyfile;
+struct TALER_MerchantPublicKeyP pubkey;
/**
- * This value tells the mint by which date this merchant would like
- * to receive the funds for a deposited payment
+ * Our hostname
*/
-struct GNUNET_TIME_Relative edate_delay;
+static char *hostname;
/**
- * To make 'TMH_PARSE_navigate_json ()' compile
+ * The port we are running on
*/
-char *TMH_mint_currency_string;
+static long long unsigned port;
/**
- * Trusted mints
+ * File holding the merchant's private key
+ */
+static char *keyfile;
+
+/**
+ * This value tells the mint by which date this merchant would like
+ * to receive the funds for a deposited payment
*/
-struct MERCHANT_Mint *mints;
+struct GNUNET_TIME_Relative edate_delay;
/**
- * Active auditors
+ * Which currency is supported by this merchant?
*/
-struct MERCHANT_Auditor *auditors;
+char *TMH_merchant_currency_string;
/**
* Shutdown task identifier
@@ -90,31 +95,6 @@ static struct GNUNET_SCHEDULER_Task *shutdown_task;
static struct GNUNET_SCHEDULER_Task *mhd_task;
/**
- * Context "poller" identifier
- */
-struct GNUNET_SCHEDULER_Task *poller_task;
-
-/**
- * Our wireformat
- */
-struct MERCHANT_WIREFORMAT_Sepa *wire;
-
-/**
- * Salt used to hash the wire object
- */
-long long salt;
-
-/**
- * The number of accepted mints
- */
-unsigned int nmints;
-
-/**
- * The number of active auditors
- */
-unsigned int nauditors;
-
-/**
* Should we do a dry run where temporary tables are used for storing the data.
*/
static int dry;
@@ -127,13 +107,14 @@ static int result;
/**
* Connection handle to the our database
*/
-PGconn *db_conn;
+struct TALER_MERCHANTDB_Plugin *db;
/**
* The MHD Daemon
*/
static struct MHD_Daemon *mhd;
+
/**
* A client has requested the given url using the given method
* (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
@@ -190,15 +171,9 @@ url_handler (void *cls,
"Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.\n", 0,
&TMH_MHD_handler_static_response, MHD_HTTP_OK },
- /* Further test page */
- { "/hello", MHD_HTTP_METHOD_GET, "text/plain",
- "Hello, Customer.\n", 0,
- &TMH_MHD_handler_static_response, MHD_HTTP_OK },
-
{ "/contract", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&MH_handler_contract, MHD_HTTP_OK },
-
{ "/contract", NULL, "text/plain",
"Only POST is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
@@ -206,33 +181,24 @@ url_handler (void *cls,
{ "/pay", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&MH_handler_pay, MHD_HTTP_OK },
-
{ "/pay", NULL, "text/plain",
"Only POST is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
{NULL, NULL, NULL, NULL, 0, 0 }
};
-
static struct TMH_RequestHandler h404 =
{
"", NULL, "text/html",
"<html><title>404: not found</title></html>", 0,
&TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
};
-
- /* Compiler complains about non returning a value in a non-void
- declared function: the FIX is to return what the handler for
- a particular URL returns */
-
struct TMH_RequestHandler *rh;
unsigned int i;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Handling request for URL '%s'\n",
+ "Handling request for URL `%s'\n",
url);
-
for (i=0;NULL != handlers[i].url;i++)
{
rh = &handlers[i];
@@ -252,43 +218,6 @@ url_handler (void *cls,
con_cls,
upload_data,
upload_data_size);
-
-}
-
-/**
- * Function called with information about who is auditing
- * a particular mint and what key the mint is using.
- *
- * @param cls closure, will be 'struct MERCHANT_Mint' so that
- * when this function gets called, it will change the flag 'pending'
- * to 'false'. Note: 'keys' is automatically saved inside the mint's
- * handle, which is contained inside 'struct MERCHANT_Mint', when
- * this callback is called. Thus, once 'pending' turns 'false',
- * it is safe to call 'TALER_MINT_get_keys()' on the mint's handle,
- * in order to get the "good" keys.
- *
- * @param keys information about the various keys used
- * by the mint
- */
-static void
-keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
-{
- /* HOT UPDATE: the merchants need the denomination keys!
- Because it wants to (firstly) verify the deposit confirmation
- sent by the mint, and the signed blob depends (among the
- other things) on the coin's deposit fee. That information
- is never communicated by the wallet to the merchant.
- Again, the merchant needs it because it wants to verify that
- the wallet didn't exceede the limit imposed by the merchant
- on the total deposit fee for a purchase */
-
- if (NULL != keys)
- {
- ((struct MERCHANT_Mint *) cls)->pending = 0;
- }
- else
- printf ("no keys gotten\n");
-
}
@@ -302,19 +231,6 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
static void
do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
- unsigned int cnt;
-
- for (cnt = 0; cnt < nmints; cnt++)
- {
- if (NULL != mints[cnt].conn)
- TALER_MINT_disconnect (mints[cnt].conn);
-
- }
- if (NULL != poller_task)
- {
- GNUNET_SCHEDULER_cancel (poller_task);
- poller_task = NULL;
- }
if (NULL != mhd_task)
{
GNUNET_SCHEDULER_cancel (mhd_task);
@@ -325,77 +241,19 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
MHD_stop_daemon (mhd);
mhd = NULL;
}
- if (NULL != db_conn)
+ if (NULL != db)
{
- MERCHANT_DB_disconnect (db_conn);
- db_conn = NULL;
+ TALER_MERCHANTDB_plugin_unload (db);
+ db = NULL;
}
+ TMH_MINTS_done ();
+ TMH_AUDITORS_done ();
if (NULL != keyfile)
GNUNET_free (privkey);
}
/**
- * Task that runs the context's event loop using the GNUnet scheduler.
- *
- * @param cls mint context
- * @param tc scheduler context (unused)
- */
-void
-context_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- long timeout;
- int max_fd;
- fd_set read_fd_set;
- fd_set write_fd_set;
- fd_set except_fd_set;
- struct GNUNET_NETWORK_FDSet *rs;
- struct GNUNET_NETWORK_FDSet *ws;
- struct GNUNET_TIME_Relative delay;
- struct TALER_MINT_Context *ctx;
-
- ctx = (struct TALER_MINT_Context *) cls;
- poller_task = NULL;
- TALER_MINT_perform (ctx);
- max_fd = -1;
- timeout = -1;
- FD_ZERO (&read_fd_set);
- FD_ZERO (&write_fd_set);
- FD_ZERO (&except_fd_set);
- TALER_MINT_get_select_info (ctx,
- &read_fd_set,
- &write_fd_set,
- &except_fd_set,
- &max_fd,
- &timeout);
- if (timeout >= 0)
- delay =
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- timeout);
- else
- delay = GNUNET_TIME_UNIT_FOREVER_REL;
- rs = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_copy_native (rs,
- &read_fd_set,
- max_fd + 1);
- ws = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_copy_native (ws,
- &write_fd_set,
- max_fd + 1);
- poller_task =
- GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- delay,
- rs,
- ws,
- &context_task,
- cls);
- GNUNET_NETWORK_fdset_destroy (rs);
- GNUNET_NETWORK_fdset_destroy (ws);
-}
-
-
-/**
* Function called whenever MHD is done with a request. If the
* request was a POST, we may have stored a `struct Buffer *` in the
* @a con_cls that might still need to be cleaned up. Call the
@@ -451,9 +309,12 @@ run_daemon (void *cls,
/**
* Kick MHD to run now, to be called after MHD_resume_connection().
+ * Basically, we need to explicitly resume MHD's event loop whenever
+ * we made progress serving a request. This function re-schedules
+ * the task processing MHD's activities to run immediately.
*/
void
-TM_trigger_daemon ()
+TMH_trigger_daemon ()
{
GNUNET_SCHEDULER_cancel (mhd_task);
run_daemon (NULL, NULL);
@@ -461,6 +322,100 @@ TM_trigger_daemon ()
/**
+ * Parse the SEPA information from the configuration. If any of the
+ * required fields is missing return an error.
+ *
+ * @param cfg the configuration
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ unsigned long long salt;
+ char *iban;
+ char *name;
+ char *bic;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "wire-sepa",
+ "SALT",
+ &salt))
+ {
+ salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
+ UINT64_MAX);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No SALT option given in `wire-sepa`, using %llu\n",
+ (unsigned long long) salt);
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "IBAN",
+ &iban))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "NAME",
+ &name))
+ {
+ GNUNET_free (iban);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "BIC",
+ &bic))
+ {
+ GNUNET_free (iban);
+ GNUNET_free (name);
+ GNUNET_free (bic);
+ }
+ j_wire = json_pack ("{s:s, s:s, s:s, s:s, s:o}",
+ "type", "SEPA",
+ "IBAN", iban,
+ "name", name,
+ "bic", bic,
+ "r", json_integer (salt));
+ GNUNET_free (iban);
+ GNUNET_free (name);
+ GNUNET_free (bic);
+ if (NULL == j_wire)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Verify that #j_wire contains a well-formed wire format, and
+ * update #h_wire to match it (if successful).
+ *
+ * @param allowed which wire format is allowed/expected?
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+validate_and_hash_wireformat (const char *allowed)
+{
+ const char *allowed_arr[] = {
+ allowed,
+ NULL
+ };
+
+ if (GNUNET_YES !=
+ TALER_json_validate_wireformat (allowed_arr,
+ j_wire))
+ return GNUNET_SYSERR;
+ if (GNUNET_SYSERR ==
+ TALER_hash_json (j_wire,
+ &h_wire))
+ return MHD_NO;
+ return GNUNET_OK;
+}
+
+
+/**
* Function that queries MHD's select sets and
* starts the task waiting for them.
*
@@ -510,55 +465,49 @@ prepare_daemon ()
}
-
/**
* Main function that will be run by the scheduler.
*
* @param cls closure
* @param args remaining command-line arguments
* @param cfgfile name of the configuration file used (for saving, can be
- * NULL!)
+ * NULL!)
* @param config configuration
*/
void
-run (void *cls, char *const *args, const char *cfgfile,
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *config)
{
-
- unsigned int cnt;
- mints = NULL;
- keyfile = NULL;
result = GNUNET_SYSERR;
shutdown_task =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &do_shutdown,
- NULL);
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO, "merchant launched\n");
-
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown,
+ NULL);
EXITIF (GNUNET_SYSERR ==
- (nmints =
- TALER_MERCHANT_parse_mints (config,
- &mints)));
+ TMH_MINTS_init (config));
EXITIF (GNUNET_SYSERR ==
- (nauditors =
- TALER_MERCHANT_parse_auditors (config,
- &auditors)));
- EXITIF (NULL ==
- (wire =
- TALER_MERCHANT_parse_wireformat_sepa (config)));
+ TMH_AUDITORS_init (config));
+ /* FIXME: for now, we just support SEPA here: */
+ EXITIF (GNUNET_OK !=
+ parse_wireformat_sepa (config));
+ EXITIF (GNUNET_OK !=
+ validate_and_hash_wireformat ("SEPA"));
EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (config,
- "merchant",
- "KEYFILE",
- &keyfile));
+ GNUNET_CONFIGURATION_get_value_filename (config,
+ "merchant",
+ "KEYFILE",
+ &keyfile));
EXITIF (NULL ==
- (privkey =
- GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ (privkey =
+ GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ GNUNET_CRYPTO_eddsa_key_get_public (privkey,
+ &pubkey.eddsa_pub);
EXITIF (NULL ==
- (db_conn = MERCHANT_DB_connect (config)));
+ (db = TALER_MERCHANTDB_plugin_load (config)));
EXITIF (GNUNET_OK !=
- MERCHANT_DB_initialize (db_conn, dry));
+ db->initialize (db->cls, dry));
EXITIF (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_number (config,
"merchant",
@@ -573,30 +522,13 @@ run (void *cls, char *const *args, const char *cfgfile,
GNUNET_CONFIGURATION_get_value_string (config,
"merchant",
"CURRENCY",
- &TMH_mint_currency_string));
+ &TMH_merchant_currency_string));
EXITIF (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_time (config,
- "merchant",
- "EDATE",
- &edate_delay));
-
- salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
- UINT64_MAX);
-
- for (cnt = 0; cnt < nmints; cnt++)
- {
- EXITIF (NULL == (mints[cnt].ctx = TALER_MINT_init ()));
- mints[cnt].pending = 1;
- mints[cnt].conn = TALER_MINT_connect (mints[cnt].ctx,
- mints[cnt].hostname,
- &keys_mgmt_cb,
- &mints[cnt],
- TALER_MINT_OPTION_END);
- EXITIF (NULL == mints[cnt].conn);
- poller_task =
- GNUNET_SCHEDULER_add_now (&context_task, mints[cnt].ctx);
- }
+ "merchant",
+ "EDATE",
+ &edate_delay));
mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME,
port,
@@ -609,15 +541,15 @@ run (void *cls, char *const *args, const char *cfgfile,
result = GNUNET_OK;
mhd_task = prepare_daemon ();
- EXITIF_exit:
- if (GNUNET_OK != result)
- GNUNET_SCHEDULER_shutdown ();
+ EXITIF_exit:
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
GNUNET_free_non_null (keyfile);
- if (GNUNET_OK != result)
- GNUNET_SCHEDULER_shutdown ();
-
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
}
+
/**
* The main function of the serve tool
*
@@ -628,21 +560,18 @@ run (void *cls, char *const *args, const char *cfgfile,
int
main (int argc, char *const *argv)
{
-
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
{'t', "temp", NULL,
gettext_noop ("Use temporary database tables"), GNUNET_NO,
&GNUNET_GETOPT_set_one, &dry},
- GNUNET_GETOPT_OPTION_END
- };
-
+ GNUNET_GETOPT_OPTION_END
+ };
if (GNUNET_OK !=
GNUNET_PROGRAM_run (argc, argv,
- "taler-merchant-http",
- "Serve merchant's HTTP interface",
+ "taler-merchant-httpd",
+ "Taler merchant's HTTP backend interface",
options, &run, NULL))
return 3;
return (GNUNET_OK == result) ? 0 : 1;
-
}
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index b60d91bd..25242617 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -18,16 +18,83 @@
* @brief HTTP serving layer mainly intended to communicate with the frontend
* @author Marcello Stanisci
*/
+#ifndef TALER_MERCHANT_HTTPD_H
+#define TALER_MERCHANT_HTTPD_H
-#include "merchant_db.h"
+#include "platform.h"
+#include "taler_merchantdb_lib.h"
+#include <microhttpd.h>
/**
- * Kick MHD to run now, to be called after MHD_resume_connection().
+ * Shorthand for exit jumps.
*/
-void
-TM_trigger_daemon (void);
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+/**
+ * @brief Struct describing an URL and the handler for it.
+ */
+struct TMH_RequestHandler
+{
+
+ /**
+ * URL the handler is for.
+ */
+ const char *url;
+
+ /**
+ * Method the handler is for, NULL for "all".
+ */
+ const char *method;
+
+ /**
+ * Mime type to use in reply (hint, can be NULL).
+ */
+ const char *mime_type;
+
+ /**
+ * Raw data for the @e handler
+ */
+ const void *data;
+
+ /**
+ * Number of bytes in @e data, 0 for 0-terminated.
+ */
+ size_t data_size;
+
+ /**
+ * Function to call to handle the request.
+ *
+ * @param rh this struct
+ * @param mime_type the @e mime_type for the reply (hint, can be NULL)
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+ int (*handler)(struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+ /**
+ * Default response code.
+ */
+ int response_code;
+};
+/**
+ * Each MHD response handler that sets the "connection_cls" to a
+ * non-NULL value must use a struct that has this struct as its first
+ * member. This struct contains a single callback, which will be
+ * invoked to clean up the memory when the contection is completed.
+ */
struct TM_HandlerContext;
/**
@@ -41,30 +108,65 @@ typedef void
(*TM_ContextCleanup)(struct TM_HandlerContext *hc);
+/**
+ * Each MHD response handler that sets the "connection_cls" to a
+ * non-NULL value must use a struct that has this struct as its first
+ * member. This struct contains a single callback, which will be
+ * invoked to clean up the memory when the contection is completed.
+ */
struct TM_HandlerContext
{
+ /**
+ * Function to execute the handler-specific cleanup of the
+ * (typically larger) context.
+ */
TM_ContextCleanup cc;
};
-extern struct MERCHANT_Mint *mints;
-extern struct MERCHANT_WIREFORMAT_Sepa *wire;
+/**
+ * Our wire format details in JSON format (with salt).
+ */
+extern json_t *j_wire;
+
+/**
+ * Hash of our wire format details as given in #j_wire.
+ */
+extern struct GNUNET_HashCode h_wire;
-extern PGconn *db_conn;
+/**
+ * Our private key (for the merchant to sign contracts).
+ */
+extern struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
-extern long long salt;
+/**
+ * Our public key, corresponds to #privkey.
+ */
+extern struct TALER_MerchantPublicKeyP pubkey;
-extern unsigned int nmints;
+/**
+ * Handle to the database backend.
+ */
+extern struct TALER_MERCHANTDB_Plugin *db;
+/**
+ * If the frontend does NOT specify an execution date, how long should
+ * we tell the mint to wait to aggregate transactions before
+ * executing? This delay is added to the current time when we
+ * generate the advisory execution time for the mint.
+ */
extern struct GNUNET_TIME_Relative edate_delay;
-extern struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
-
-extern struct GNUNET_SCHEDULER_Task *poller_task;
+/**
+ * Kick MHD to run now, to be called after MHD_resume_connection().
+ * Basically, we need to explicitly resume MHD's event loop whenever
+ * we made progress serving a request. This function re-schedules
+ * the task processing MHD's activities to run immediately.
+ */
+void
+TMH_trigger_daemon (void);
-void
-context_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc);
+#endif
diff --git a/src/backend/taler-merchant-httpd_auditors.c b/src/backend/taler-merchant-httpd_auditors.c
new file mode 100644
index 00000000..84558ed0
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_auditors.c
@@ -0,0 +1,239 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_auditors.c
+ * @brief logic this HTTPD keeps for each mint we interact with
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_auditors.h"
+
+/**
+ * Our representation of an auditor.
+ */
+struct Auditor
+{
+ /**
+ * Auditor's legal name.
+ */
+ char *name;
+
+ /**
+ * Auditor's URI.
+ */
+ char *uri;
+
+ /**
+ * Public key of the auditor.
+ */
+ struct TALER_AuditorPublicKeyP public_key;
+
+};
+
+
+/**
+ * Array of the auditors this merchant is willing to accept.
+ */
+static struct Auditor *auditors;
+
+/**
+ * The length of the #auditors array.
+ */
+static unsigned int nauditors;
+
+/**
+ * JSON representation of the auditors accepted by this mint.
+ */
+json_t *j_auditors;
+
+
+/**
+ * Check if the given @a dk issued by mint @a mh is audited by
+ * an auditor that is acceptable for this merchant. (And if the
+ * denomination is not yet expired or something silly like that.)
+ *
+ * @param mh mint issuing @a dk
+ * @param dk a denomination issued by @a mh
+ * @param mint_trusted #GNUNET_YES if the mint of @a dk is trusted by config
+ * @return #GNUNET_OK if we accept this denomination
+ */
+int
+TMH_AUDITORS_check_dk (struct TALER_MINT_Handle *mh,
+ const struct TALER_MINT_DenomPublicKey *dk,
+ int mint_trusted)
+{
+ const struct TALER_MINT_Keys *keys;
+ const struct TALER_MINT_AuditorInformation *ai;
+ unsigned int i;
+ unsigned int j;
+
+ if (0 == GNUNET_TIME_absolute_get_remaining (dk->deposit_valid_until).rel_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Denomination key offered by client has expired for deposits\n");
+ return GNUNET_SYSERR; /* expired */
+ }
+ if (GNUNET_YES == mint_trusted)
+ return GNUNET_OK;
+ keys = TALER_MINT_get_keys (mh);
+ if (NULL == keys)
+ {
+ /* this should never happen, keys should have been successfully
+ obtained before we even got into this function */
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ for (i=0;i<keys->num_auditors;i++)
+ {
+ ai = &keys->auditors[i];
+ for (j=0;j<ai->num_denom_keys;j++)
+ if (ai->denom_keys[j] == dk)
+ return GNUNET_OK;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Denomination key %s offered by client not audited by accepted auditor\n",
+ GNUNET_h2s (&dk->h_key));
+ return GNUNET_NO;
+}
+
+
+/**
+ * Function called on each configuration section. Finds sections
+ * about auditors and parses the entries.
+ *
+ * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
+ * @param section name of the section
+ */
+static void
+parse_auditors (void *cls,
+ const char *section)
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ char *pks;
+ struct Auditor auditor;
+
+ if (0 != strncasecmp (section,
+ "auditor-",
+ strlen ("auditor-")))
+ return;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "NAME",
+ &auditor.name))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "NAME");
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "URI",
+ &auditor.uri))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "URI");
+ GNUNET_free (auditor.name);
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "PUBLIC_KEY",
+ &pks))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "PUBLIC_KEY");
+ GNUNET_free (auditor.name);
+ GNUNET_free (auditor.uri);
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (pks,
+ strlen (pks),
+ &auditor.public_key.eddsa_pub))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "PUBLIC_KEY",
+ "valid public key");
+ GNUNET_free (auditor.name);
+ GNUNET_free (auditor.uri);
+ GNUNET_free (pks);
+ return;
+ }
+ GNUNET_free (pks);
+ GNUNET_array_append (auditors,
+ nauditors,
+ auditor);
+}
+
+
+/**
+ * Parses auditor information from the configuration.
+ *
+ * @param cfg the configuration
+ * @return the number of auditors found; #GNUNET_SYSERR upon error in
+ * parsing.
+ */
+int
+TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ unsigned int cnt;
+
+ GNUNET_CONFIGURATION_iterate_sections (cfg,
+ &parse_auditors,
+ (void *) cfg);
+
+ /* Generate preferred mint(s) array. */
+ j_auditors = json_array ();
+ for (cnt = 0; cnt < nauditors; cnt++)
+ json_array_append_new (j_auditors,
+ json_pack ("{s:s, s:o, s:s}",
+ "name", auditors[cnt].name,
+ "auditor_pub", TALER_json_from_data (&auditors[cnt].public_key,
+ sizeof (struct TALER_AuditorPublicKeyP)),
+ "uri", auditors[cnt].uri));
+ return nauditors;
+}
+
+
+/**
+ * Release auditor information state.
+ */
+void
+TMH_AUDITORS_done ()
+{
+ unsigned int i;
+
+ json_decref (j_auditors);
+ j_auditors = NULL;
+ for (i=0;i<nauditors;i++)
+ {
+ GNUNET_free (auditors[i].name);
+ GNUNET_free (auditors[i].uri);
+ }
+ GNUNET_free (auditors);
+ auditors = NULL;
+ nauditors = 0;
+}
+
+/* end of taler-merchant-httpd_auditors.c */
diff --git a/src/backend/taler-merchant-httpd_auditors.h b/src/backend/taler-merchant-httpd_auditors.h
new file mode 100644
index 00000000..1a05a78d
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_auditors.h
@@ -0,0 +1,74 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_auditors.h
+ * @brief logic this HTTPD keeps for each mint we interact with
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_AUDITORS_H
+#define TALER_MERCHANT_HTTPD_AUDITORS_H
+
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <taler/taler_util.h>
+#include <taler/taler_mint_service.h>
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * JSON representation of the auditors accepted by this mint.
+ */
+extern json_t *j_auditors;
+
+
+/**
+ * Parses auditor information from the configuration.
+ *
+ * @param cfg the configuration
+ * @return the number of auditors found; #GNUNET_SYSERR upon error in
+ * parsing.
+ */
+int
+TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Check if the given @a dk issued by mint @a mh is audited by
+ * an auditor that is acceptable for this merchant. (And if the
+ * denomination is not yet expired or something silly like that.)
+ *
+ * @param mh mint issuing @a dk
+ * @param dk a denomination issued by @a mh
+ * @param mint_trusted #GNUNET_YES if the mint of @a dk is trusted by config
+ * @return #GNUNET_OK if we accept this denomination
+ */
+int
+TMH_AUDITORS_check_dk (struct TALER_MINT_Handle *mh,
+ const struct TALER_MINT_DenomPublicKey *dk,
+ int mint_trusted);
+
+
+/**
+ * Release auditor information state.
+ */
+void
+TMH_AUDITORS_done (void);
+
+
+
+
+#endif
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
index c42a8b37..e44d3b84 100644
--- a/src/backend/taler-merchant-httpd_contract.c
+++ b/src/backend/taler-merchant-httpd_contract.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -13,39 +13,30 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
-
/**
- * @file merchant/backend/taler-merchant-httpd.c
+ * @file backend/taler-merchant-httpd_contract.c
* @brief HTTP serving layer mainly intended to communicate with the frontend
* @author Marcello Stanisci
*/
-
#include "platform.h"
-#include <microhttpd.h>
#include <jansson.h>
-#include <gnunet/gnunet_util_lib.h>
-#include <curl/curl.h>
#include <taler/taler_signatures.h>
-#include <taler/taler_amount_lib.h>
-#include <taler/taler_json_lib.h>
-#include <taler/taler_mint_service.h>
-#include "taler-mint-httpd.h"
-#include "taler-mint-httpd_parsing.h"
-#include "taler-mint-httpd_responses.h"
-#include "merchant_db.h"
-#include "merchant.h"
-#include "taler_merchant_lib.h"
#include "taler-merchant-httpd.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_auditors.h"
+#include "taler-merchant-httpd_mints.h"
+#include "taler-merchant-httpd_responses.h"
-extern struct MERCHANT_Auditor *auditors;
-extern unsigned int nauditors;
/**
- * Manage a contract request. In practical terms, it adds the fields 'mints',
- * 'merchant_pub', and 'H_wire' to the contract 'proposition' gotten from the
- * frontend. Finally, it adds (outside of the contract) a signature of the
- * (hashed stringification) of this contract and the hashed stringification
- * of this contract to the final bundle sent back to the frontend.
+ * Manage a contract request. In practical terms, it adds the fields
+ * 'mints', 'merchant_pub', and 'H_wire' to the contract 'proposition'
+ * gotten from the frontend. Finally, it adds (outside of the
+ * contract) a signature of the (hashed stringification) of the
+ * contract (and the hashed stringification of this contract as well
+ * to aid diagnostics) to the final bundle, which is then send back to
+ * the frontend.
+ *
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
@@ -61,18 +52,8 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
size_t *upload_data_size)
{
json_t *root;
- json_t *trusted_mints;
- json_t *j_auditors;
- json_t *auditor;
- json_t *mint;
- json_t *j_wire;
- const struct TALER_MINT_Keys *keys;
int res;
- int cnt;
- struct GNUNET_HashCode h_wire;
- struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
- struct MERCHANT_Contract contract;
- char *contract_str;
+ struct TALER_ContractPS contract;
struct GNUNET_CRYPTO_EddsaSignature contract_sig;
res = TMH_PARSE_post_json (connection,
@@ -82,87 +63,43 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
&root);
if (GNUNET_SYSERR == res)
return MHD_NO;
- /* the POST's body has to be further fetched */ if ((GNUNET_NO == res) || (NULL == root))
+ /* the POST's body has to be further fetched */
+ if ((GNUNET_NO == res) || (NULL == root))
return MHD_YES;
- /* Generate preferred mint(s) array. */
-
- trusted_mints = json_array ();
- for (cnt = 0; cnt < nmints; cnt++)
- {
- if (!mints[cnt].pending)
- {
- keys = TALER_MINT_get_keys (mints[cnt].conn);
- mint = json_pack ("{s:s, s:o}",
- "url", mints[cnt].hostname,
- "master_pub",
- TALER_json_from_data
- (&keys->master_pub.eddsa_pub,
- sizeof (keys->master_pub.eddsa_pub)));
- json_array_append_new (trusted_mints, mint);
- }
- }
- j_auditors = json_array ();
- for (cnt = 0; cnt < nauditors; cnt++)
- {
- auditor = json_pack ("{s:s}",
- "name", auditors[cnt].name);
- json_array_append_new (j_auditors, auditor);
- }
-
- /**
- * Return badly if no mints are trusted (or no call to /keys has still
- * returned the expected data). WARNING: it
- * may be possible that a mint trusted by the wallet is good, but
- * still pending; that case must be handled with some "polling-style"
- * routine, simply ignored, or ended with an invitation to the wallet
- * to just retry later
- */
- if (!json_array_size (trusted_mints))
- return MHD_NO;
-
- /**
- * Hard error, no action can be taken by a wallet
- */
- if (!json_array_size (j_auditors))
- return MHD_NO;
-
- json_object_set_new (root, "mints", trusted_mints);
- json_object_set_new (root, "auditors", j_auditors);
-
- if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
- salt)))
- return MHD_NO;
-
- /* hash wire objcet */
- if (GNUNET_SYSERR ==
- TALER_hash_json (j_wire, &h_wire))
- return MHD_NO;
-
+ /* add fields to the "root" that the backend should provide */
+ json_object_set (root,
+ "mints",
+ trusted_mints);
+ json_object_set (root,
+ "auditors",
+ j_auditors);
json_object_set_new (root,
"H_wire",
- TALER_json_from_data (&h_wire, sizeof (h_wire)));
-
- GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pubkey);
+ TALER_json_from_data (&h_wire,
+ sizeof (h_wire)));
json_object_set_new (root,
"merchant_pub",
- TALER_json_from_data (&pubkey, sizeof (pubkey)));
-
- /* Sign */
- contract_str = json_dumps (root, JSON_COMPACT | JSON_SORT_KEYS);
- GNUNET_CRYPTO_hash (contract_str, strlen (contract_str), &contract.h_contract);
+ TALER_json_from_data (&pubkey,
+ sizeof (pubkey)));
+ /* create contract signature */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_hash_json (root,
+ &contract.h_contract));
contract.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
contract.purpose.size = htonl (sizeof (contract));
- GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &contract_sig);
-
+ GNUNET_CRYPTO_eddsa_sign (privkey,
+ &contract.purpose,
+ &contract_sig);
+ /* return final response */
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:o, s:o}",
"contract", root,
- "sig", TALER_json_from_data
- (&contract_sig, sizeof (contract_sig)),
- "H_contract", TALER_json_from_data
- (&contract.h_contract,
- sizeof (contract.h_contract)));
-
+ "sig", TALER_json_from_data (&contract_sig,
+ sizeof (contract_sig)),
+ "H_contract", TALER_json_from_data (&contract.h_contract,
+ sizeof (contract.h_contract)));
}
+
+/* end of taler-merchant-httpd_contract.c */
diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h
index 5e72c514..44cef909 100644
--- a/src/backend/taler-merchant-httpd_contract.h
+++ b/src/backend/taler-merchant-httpd_contract.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -13,17 +13,15 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
-
/**
- * @file merchant/backend/taler-merchant-httpd_contract.h
+ * @file backend/taler-merchant-httpd_contract.h
* @brief headers for /contract handler
* @author Marcello Stanisci
*/
-
#ifndef TALER_MINT_HTTPD_CONTRACT_H
#define TALER_MINT_HTTPD_CONTRACT_H
#include <microhttpd.h>
-#include "taler-mint-httpd.h"
+#include "taler-merchant-httpd.h"
/**
* Manage a contract request
@@ -33,7 +31,6 @@
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- *
* @return MHD result code
*/
int
diff --git a/src/backend/taler-mint-httpd_mhd.c b/src/backend/taler-merchant-httpd_mhd.c
index 419c4fb0..6fd18d9c 100644
--- a/src/backend/taler-mint-httpd_mhd.c
+++ b/src/backend/taler-merchant-httpd_mhd.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014 GNUnet e.V.
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -13,9 +13,8 @@
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
-
/**
- * @file taler-mint-httpd_mhd.c
+ * @file taler-merchant-httpd_mhd.c
* @brief helpers for MHD interaction; these are TALER_MINT_handler_ functions
* that generate simple MHD replies that do not require any real operations
* to be performed (error handling, static pages, etc.)
@@ -24,14 +23,9 @@
* @author Christian Grothoff
*/
#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
-#include <microhttpd.h>
-#include <pthread.h>
-#include "taler-mint-httpd_responses.h"
-#include "taler-mint-httpd.h"
-#include "taler-mint-httpd_mhd.h"
-#include "taler-mint-httpd_responses.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_responses.h"
/**
diff --git a/src/backend/taler-mint-httpd_mhd.h b/src/backend/taler-merchant-httpd_mhd.h
index a9f575df..3fe137db 100644
--- a/src/backend/taler-mint-httpd_mhd.h
+++ b/src/backend/taler-merchant-httpd_mhd.h
@@ -15,7 +15,7 @@
*/
/**
- * @file taler-mint-httpd_mhd.h
+ * @file taler-merchant-httpd_mhd.h
* @brief helpers for MHD interaction, used to generate simple responses
* @author Florian Dold
* @author Benedikt Mueller
@@ -25,7 +25,7 @@
#define TALER_MINT_HTTPD_MHD_H
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
-#include "taler-mint-httpd.h"
+#include "taler-merchant-httpd.h"
/**
diff --git a/src/backend/taler-merchant-httpd_mints.c b/src/backend/taler-merchant-httpd_mints.c
new file mode 100644
index 00000000..e93419cd
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_mints.c
@@ -0,0 +1,530 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_mints.c
+ * @brief logic this HTTPD keeps for each mint we interact with
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_mints.h"
+
+
+/**
+ * How often do we retry fetching /keys?
+ */
+#define KEYS_RETRY_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
+
+
+/**
+ * Mint
+ */
+struct Mint;
+
+
+/**
+ * Information we keep for a pending #MMH_MINTS_find_mint() operation.
+ */
+struct TMH_MINTS_FindOperation
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct TMH_MINTS_FindOperation *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct TMH_MINTS_FindOperation *prev;
+
+ /**
+ * Function to call with the result.
+ */
+ TMH_MINTS_FindContinuation fc;
+
+ /**
+ * Closure for @e fc.
+ */
+ void *fc_cls;
+
+ /**
+ * Mint we wait for the /keys for.
+ */
+ struct Mint *my_mint;
+
+ /**
+ * Task scheduled to asynchrnously return the result.
+ */
+ struct GNUNET_SCHEDULER_Task *at;
+
+};
+
+
+/**
+ * Mint
+ */
+struct Mint
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct Mint *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct Mint *prev;
+
+ /**
+ * Head of FOs pending for this mint.
+ */
+ struct TMH_MINTS_FindOperation *fo_head;
+
+ /**
+ * Tail of FOs pending for this mint.
+ */
+ struct TMH_MINTS_FindOperation *fo_tail;
+
+ /**
+ * (base) URI of the mint.
+ */
+ char *uri;
+
+ /**
+ * A connection to this mint
+ */
+ struct TALER_MINT_Handle *conn;
+
+ /**
+ * Master public key, guaranteed to be set ONLY for
+ * trusted mints.
+ */
+ struct TALER_MasterPublicKeyP master_pub;
+
+ /**
+ * At what time should we try to fetch /keys again?
+ */
+ struct GNUNET_TIME_Absolute retry_time;
+
+ /**
+ * Flag which indicates whether some HTTP transfer between
+ * this merchant and the mint is still ongoing
+ */
+ int pending;
+
+ /**
+ * #GNUNET_YES if this mint is from our configuration and
+ * explicitly trusted, #GNUNET_NO if we need to check each
+ * key to be sure it is trusted.
+ */
+ int trusted;
+
+};
+
+
+/**
+ * Context for all mint operations (useful to the event loop)
+ */
+static struct TALER_MINT_Context *ctx;
+
+/**
+ * Task we use to drive the interaction with this mint.
+ */
+static struct GNUNET_SCHEDULER_Task *poller_task;
+
+/**
+ * Head of mints we know about.
+ */
+static struct Mint *mint_head;
+
+/**
+ * Tail of mints we know about.
+ */
+static struct Mint *mint_tail;
+
+/**
+ * List of our trusted mints for inclusion in contracts.
+ */
+json_t *trusted_mints;
+
+
+/**
+ * Function called with information about who is auditing
+ * a particular mint and what key the mint is using.
+ *
+ * @param cls closure, will be `struct Mint` so that
+ * when this function gets called, it will change the flag 'pending'
+ * to 'false'. Note: 'keys' is automatically saved inside the mint's
+ * handle, which is contained inside 'struct Mint', when
+ * this callback is called. Thus, once 'pending' turns 'false',
+ * it is safe to call 'TALER_MINT_get_keys()' on the mint's handle,
+ * in order to get the "good" keys.
+ * @param keys information about the various keys used
+ * by the mint
+ */
+static void
+keys_mgmt_cb (void *cls,
+ const struct TALER_MINT_Keys *keys)
+{
+ struct Mint *mint = cls;
+ struct TMH_MINTS_FindOperation *fo;
+
+ if (NULL != keys)
+ {
+ mint->pending = GNUNET_NO;
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to fetch /keys from `%s'\n",
+ mint->uri);
+ TALER_MINT_disconnect (mint->conn);
+ mint->conn = NULL;
+ mint->pending = GNUNET_SYSERR; /* failed hard */
+ mint->retry_time = GNUNET_TIME_relative_to_absolute (KEYS_RETRY_FREQ);
+ }
+ while (NULL != (fo = mint->fo_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (mint->fo_head,
+ mint->fo_tail,
+ fo);
+ fo->fc (fo->fc_cls,
+ (NULL != keys) ? mint->conn : NULL,
+ mint->trusted);
+ GNUNET_free (fo);
+ }
+}
+
+
+/**
+ * Task that runs the mint's event loop using the GNUnet scheduler.
+ *
+ * @param cls a `struct Mint *`
+ * @param tc scheduler context (unused)
+ */
+static void
+context_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ long timeout;
+ int max_fd;
+ fd_set read_fd_set;
+ fd_set write_fd_set;
+ fd_set except_fd_set;
+ struct GNUNET_NETWORK_FDSet *rs;
+ struct GNUNET_NETWORK_FDSet *ws;
+ struct GNUNET_TIME_Relative delay;
+
+ poller_task = NULL;
+ TALER_MINT_perform (ctx);
+ max_fd = -1;
+ timeout = -1;
+ FD_ZERO (&read_fd_set);
+ FD_ZERO (&write_fd_set);
+ FD_ZERO (&except_fd_set);
+ TALER_MINT_get_select_info (ctx,
+ &read_fd_set,
+ &write_fd_set,
+ &except_fd_set,
+ &max_fd,
+ &timeout);
+ if (timeout >= 0)
+ delay =
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ timeout);
+ else
+ delay = GNUNET_TIME_UNIT_FOREVER_REL;
+ rs = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (rs,
+ &read_fd_set,
+ max_fd + 1);
+ ws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (ws,
+ &write_fd_set,
+ max_fd + 1);
+ poller_task =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ delay,
+ rs,
+ ws,
+ &context_task,
+ NULL);
+ GNUNET_NETWORK_fdset_destroy (rs);
+ GNUNET_NETWORK_fdset_destroy (ws);
+}
+
+
+/**
+ * Task to return find operation result asynchronously to caller.
+ *
+ * @param cls a `struct TMH_MINTS_FindOperation`
+ * @param tc unused
+ */
+static void
+return_result (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct TMH_MINTS_FindOperation *fo = cls;
+ struct Mint *mint = fo->my_mint;
+
+ fo->at = NULL;
+ GNUNET_CONTAINER_DLL_remove (mint->fo_head,
+ mint->fo_tail,
+ fo);
+ fo->fc (fo->fc_cls,
+ (GNUNET_SYSERR == mint->pending) ? NULL : mint->conn,
+ mint->trusted);
+ GNUNET_free (fo);
+ GNUNET_SCHEDULER_cancel (poller_task);
+ GNUNET_SCHEDULER_add_now (&context_task,
+ NULL);
+}
+
+
+/**
+ * Find a mint that matches @a chosen_mint. If we cannot connect
+ * to the mint, or if it is not acceptable, @a fc is called with
+ * NULL for the mint.
+ *
+ * @param chosen_mint URI of the mint we would like to talk to
+ * @param fc function to call with the handles for the mint
+ * @param fc_cls closure for @a fc
+ * @return NULL on error
+ */
+struct TMH_MINTS_FindOperation *
+TMH_MINTS_find_mint (const char *chosen_mint,
+ TMH_MINTS_FindContinuation fc,
+ void *fc_cls)
+{
+ struct Mint *mint;
+ struct TMH_MINTS_FindOperation *fo;
+
+ if (NULL == ctx)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ /* Check if the mint is known */
+ for (mint = mint_head; NULL != mint; mint = mint->next)
+ /* test it by checking public key --- FIXME: hostname or public key!?
+ Should probably be URI, not hostname anyway! */
+ if (0 == strcmp (mint->uri,
+ chosen_mint))
+ break;
+ if (NULL == mint)
+ {
+ /* This is a new mint */
+ mint = GNUNET_new (struct Mint);
+ mint->uri = GNUNET_strdup (chosen_mint);
+ mint->pending = GNUNET_YES;
+ GNUNET_CONTAINER_DLL_insert (mint_head,
+ mint_tail,
+ mint);
+ }
+
+ /* check if we should resume this mint */
+ if ( (GNUNET_SYSERR == mint->pending) &&
+ (0 == GNUNET_TIME_absolute_get_remaining (mint->retry_time).rel_value_us) )
+ mint->pending = GNUNET_YES;
+
+
+ fo = GNUNET_new (struct TMH_MINTS_FindOperation);
+ fo->fc = fc;
+ fo->fc_cls = fc_cls;
+ fo->my_mint = mint;
+ GNUNET_CONTAINER_DLL_insert (mint->fo_head,
+ mint->fo_tail,
+ fo);
+
+ if (GNUNET_NO == mint->pending)
+ {
+ /* We are not currently waiting for a reply, immediately
+ return result */
+ fo->at = GNUNET_SCHEDULER_add_now (&return_result,
+ fo);
+ return fo;
+ }
+
+ /* If new or resumed, retry fetching /keys */
+ if ( (NULL == mint->conn) &&
+ (GNUNET_YES == mint->pending) )
+ {
+ mint->conn = TALER_MINT_connect (ctx,
+ mint->uri,
+ &keys_mgmt_cb,
+ mint,
+ TALER_MINT_OPTION_END);
+ GNUNET_break (NULL != mint->conn);
+ }
+ return fo;
+}
+
+
+/**
+ * Abort pending find operation.
+ *
+ * @param fo handle to operation to abort
+ */
+void
+TMH_MINTS_find_mint_cancel (struct TMH_MINTS_FindOperation *fo)
+{
+ struct Mint *mint = fo->my_mint;
+
+ if (NULL != fo->at)
+ {
+ GNUNET_SCHEDULER_cancel (fo->at);
+ fo->at = NULL;
+ }
+ GNUNET_CONTAINER_DLL_remove (mint->fo_head,
+ mint->fo_tail,
+ fo);
+ GNUNET_free (fo);
+}
+
+
+/**
+ * Function called on each configuration section. Finds sections
+ * about mints and parses the entries.
+ *
+ * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
+ * @param section name of the section
+ */
+static void
+parse_mints (void *cls,
+ const char *section)
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ char *uri;
+ char *mks;
+ struct Mint *mint;
+
+ if (0 != strncasecmp (section,
+ "mint-",
+ strlen ("mint-")))
+ return;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "URI",
+ &uri))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "URI");
+ return;
+ }
+ mint = GNUNET_new (struct Mint);
+ mint->uri = uri;
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "MASTER_KEY",
+ &mks))
+ {
+ if (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_public_key_from_string (mks,
+ strlen (mks),
+ &mint->master_pub.eddsa_pub))
+ {
+ mint->trusted = GNUNET_YES;
+ }
+ else
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "MASTER_KEY",
+ _("ill-formed key"));
+ }
+ GNUNET_free (mks);
+ }
+ GNUNET_CONTAINER_DLL_insert (mint_head,
+ mint_tail,
+ mint);
+ mint->pending = GNUNET_YES;
+ mint->conn = TALER_MINT_connect (ctx,
+ mint->uri,
+ &keys_mgmt_cb,
+ mint,
+ TALER_MINT_OPTION_END);
+ GNUNET_break (NULL != mint->conn);
+}
+
+
+/**
+ * Parses "trusted" mints listed in the configuration.
+ *
+ * @param cfg the configuration
+ * @return #GNUNET_OK on success; #GNUNET_SYSERR upon error in
+ * parsing.
+ */
+int
+TMH_MINTS_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct Mint *mint;
+ json_t *j_mint;
+
+ ctx = TALER_MINT_init ();
+ if (NULL == ctx)
+ return GNUNET_SYSERR;
+ GNUNET_CONFIGURATION_iterate_sections (cfg,
+ &parse_mints,
+ (void *) cfg);
+ /* build JSON with list of trusted mints */
+ trusted_mints = json_array ();
+ for (mint = mint_head; NULL != mint; mint = mint->next)
+ {
+ if (GNUNET_YES != mint->trusted)
+ continue;
+ j_mint = json_pack ("{s:s, s:o}",
+ "url", mint->uri,
+ "master_pub", TALER_json_from_data (&mint->master_pub,
+ sizeof (struct TALER_MasterPublicKeyP)));
+ json_array_append_new (trusted_mints,
+ j_mint);
+ }
+ poller_task = GNUNET_SCHEDULER_add_now (&context_task,
+ NULL);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called to shutdown the mints subsystem.
+ */
+void
+TMH_MINTS_done ()
+{
+ struct Mint *mint;
+
+ while (NULL != (mint = mint_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (mint_head,
+ mint_tail,
+ mint);
+ if (NULL != mint->conn)
+ TALER_MINT_disconnect (mint->conn);
+ GNUNET_free (mint->uri);
+ GNUNET_free (mint);
+ }
+ if (NULL != poller_task)
+ {
+ GNUNET_SCHEDULER_cancel (poller_task);
+ poller_task = NULL;
+ }
+ TALER_MINT_fini (ctx);
+}
diff --git a/src/backend/taler-merchant-httpd_mints.h b/src/backend/taler-merchant-httpd_mints.h
new file mode 100644
index 00000000..a4684f59
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_mints.h
@@ -0,0 +1,105 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_mints.h
+ * @brief logic this HTTPD keeps for each mint we interact with
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_MINTS_H
+#define TALER_MERCHANT_HTTPD_MINTS_H
+
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <curl/curl.h>
+#include <taler/taler_util.h>
+#include <taler/taler_mint_service.h>
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * List of our trusted mints in JSON format for inclusion in contracts.
+ */
+extern json_t *trusted_mints;
+
+
+/**
+ * Parses "trusted" mints listed in the configuration.
+ *
+ * @param cfg the configuration
+ * @return #GNUNET_OK on success; #GNUNET_SYSERR upon error in
+ * parsing or initialization.
+ */
+int
+TMH_MINTS_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Function called to shutdown the mints subsystem.
+ */
+void
+TMH_MINTS_done (void);
+
+
+/**
+ * Function called with the result of a #TMH_MINTS_find_mint()
+ * operation.
+ *
+ * @param cls closure
+ * @param mh handle to the mint context
+ * @param mint_trusted #GNUNET_YES if this mint is trusted by config
+ */
+typedef void
+(*TMH_MINTS_FindContinuation)(void *cls,
+ struct TALER_MINT_Handle *mh,
+ int mint_trusted);
+
+
+/**
+ * Information we keep for a pending #MMH_MINTS_find_mint() operation.
+ */
+struct TMH_MINTS_FindOperation;
+
+
+/**
+ * Find a mint that matches @a chosen_mint. If we cannot connect
+ * to the mint, or if it is not acceptable, @a fc is called with
+ * NULL for the mint.
+ *
+ * @param chosen_mint URI of the mint we would like to talk to
+ * @param fc function to call with the handles for the mint
+ * @param fc_cls closure for @a fc
+ *
+ * FIXME: should probably return a value to *cancel* the
+ * operation in case MHD connection goes down and needs to
+ * free fc_cls.
+ */
+struct TMH_MINTS_FindOperation *
+TMH_MINTS_find_mint (const char *chosen_mint,
+ TMH_MINTS_FindContinuation fc,
+ void *fc_cls);
+
+
+/**
+ * Abort pending find operation.
+ *
+ * @param fo handle to operation to abort
+ */
+void
+TMH_MINTS_find_mint_cancel (struct TMH_MINTS_FindOperation *fo);
+
+
+#endif
diff --git a/src/backend/taler-mint-httpd_parsing.c b/src/backend/taler-merchant-httpd_parsing.c
index 9efd6c23..14a87ff4 100644
--- a/src/backend/taler-mint-httpd_parsing.c
+++ b/src/backend/taler-merchant-httpd_parsing.c
@@ -21,19 +21,18 @@
* @author Benedikt Mueller
* @author Christian Grothoff
*/
-
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
-#include "taler-mint-httpd_parsing.h"
-#include "taler-mint-httpd_responses.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_responses.h"
/* Although the following declaration isn't in any case useful
to a merchant's activity, it's needed here to make the function
'TMH_PARSE_nagivate_json ()' compile fine; so its value will be
kept on some merchant's accepted currency. For multi currencies
merchants, that of course would require a patch */
+extern char *TMH_merchant_currency_string;
-extern char *TMH_mint_currency_string;
/**
* Initial size for POST request buffer.
*/
@@ -145,6 +144,7 @@ buffer_append (struct Buffer *buf,
return GNUNET_OK;
}
+
/**
* Function called whenever we are done with a request
* to clean up our state.
@@ -164,6 +164,7 @@ TMH_PARSE_post_cleanup_callback (void *con_cls)
}
}
+
/**
* Release all memory allocated for the variable-size fields in
* the parser specification.
@@ -249,6 +250,7 @@ release_data (struct TMH_PARSE_FieldSpecification *spec,
}
}
+
/**
* Process a POST request containing a JSON object. This function
* realizes an MHD POST processor that will (incrementally) process
@@ -350,6 +352,7 @@ TMH_PARSE_post_json (struct MHD_Connection *connection,
return GNUNET_YES;
}
+
/**
* Generate line in parser specification for string. The returned
* string is already nul-terminated internally by JSON, so no length
@@ -634,7 +637,7 @@ TMH_PARSE_navigate_json (struct MHD_Connection *connection,
case TMH_PARSE_JNC_RET_STRING:
{
- void **where = va_arg (argp, void **);
+ void **where = va_arg (argp, void **);
*where = (void*) json_string_value (root);
ret = GNUNET_OK;
}
@@ -876,7 +879,7 @@ TMH_PARSE_navigate_json (struct MHD_Connection *connection,
break;
}
if (0 != strcmp (where->currency,
- TMH_mint_currency_string))
+ TMH_merchant_currency_string))
{
GNUNET_break_op (0);
ret = (MHD_YES !=
diff --git a/src/backend/taler-mint-httpd_parsing.h b/src/backend/taler-merchant-httpd_parsing.h
index dae65092..dae65092 100644
--- a/src/backend/taler-mint-httpd_parsing.h
+++ b/src/backend/taler-merchant-httpd_parsing.h
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
index 1951d2d0..319a7001 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -14,81 +14,86 @@
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file merchant/backend/taler-merchant-httpd.c
- * @brief HTTP serving layer mainly intended to communicate with the frontend
+ * @file backend/taler-merchant-httpd_pay.c
+ * @brief handling of /pay requests
* @author Marcello Stanisci
+ * @author Christian Grothoff
*/
-
#include "platform.h"
-#include <microhttpd.h>
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
-#include <curl/curl.h>
#include <taler/taler_signatures.h>
-#include <taler/taler_amount_lib.h>
#include <taler/taler_json_lib.h>
#include <taler/taler_mint_service.h>
#include "taler-merchant-httpd.h"
-#include "taler-mint-httpd.h"
-#include "taler-mint-httpd_parsing.h"
-#include "taler-mint-httpd_responses.h"
-#include "taler-mint-httpd_mhd.h"
-#include "merchant_db.h"
-#include "merchant.h"
-#include "taler_merchant_lib.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_responses.h"
+#include "taler-merchant-httpd_auditors.h"
+#include "taler-merchant-httpd_mints.h"
+#include "taler_merchantdb_lib.h"
+
+
+/**
+ * Information we keep for an individual call to the /pay handler.
+ */
+struct PayContext;
/**
- * Fetch the deposit fee related to the given coin aggregate.
- * @param connection the connection to send an error response to
- * @param coin_aggregate a coin "aggregate" is the JSON set of
- * values contained in a single cell of the 'coins' array sent
- * in a payment
- * @param deposit_fee where to store the resulting deposit fee
- * @param mint_index the index which points the chosen mint within
- * the global 'mints' array
- * @return GNUNET_OK if successful, GNUNET_NO if the data supplied
- * is invalid (including the case when the key is not found),
- * GNUNET_SYSERR upon internal errors
+ * Information kept during a /pay request for each coin.
*/
-static int
-deposit_fee_from_coin_aggregate (struct MHD_Connection *connection,
- json_t *coin_aggregate,
- struct TALER_Amount *deposit_fee,
- unsigned int mint_index)
+struct MERCHANT_DepositConfirmation
{
- int res;
- const struct TALER_MINT_Keys *keys;
- const struct TALER_MINT_DenomPublicKey *denom_details;
+
+ /**
+ * Reference to the main PayContext
+ */
+ struct PayContext *pc;
+
+ /**
+ * Handle to the deposit operation we are performing for
+ * this coin, NULL after the operation is done.
+ */
+ struct TALER_MINT_DepositHandle *dh;
+
+ /**
+ * Denomination of this coin.
+ */
struct TALER_DenominationPublicKey denom;
- struct TMH_PARSE_FieldSpecification spec[] = {
- TMH_PARSE_member_denomination_public_key ("denom_pub", &denom),
- TMH_PARSE_MEMBER_END
- };
-
- res = TMH_PARSE_json_data (connection,
- coin_aggregate,
- spec);
- if (GNUNET_OK != res)
- return res; /* may return GNUNET_NO */
-
- if (1 == mints[mint_index].pending)
- return GNUNET_SYSERR;
- keys = TALER_MINT_get_keys (mints[mint_index].conn);
- denom_details = TALER_MINT_get_denomination_key (keys, &denom);
- if (NULL == denom_details)
- {
- TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_BAD_REQUEST,
- "{s:s, s:o}",
- "hint", "unknown denom to mint",
- "denom_pub", TALER_json_from_rsa_public_key (denom.rsa_public_key));
- return GNUNET_NO;
- }
- *deposit_fee = denom_details->fee_deposit;
- return GNUNET_OK;
-}
+ /**
+ * Amount "f" that this coin contributes to the overall payment.
+ * This amount includes the deposit fee.
+ */
+ struct TALER_Amount percoin_amount;
+
+ /**
+ * Amount this coin contributes to the total purchase price.
+ */
+ struct TALER_Amount amount_without_fee;
+
+ /**
+ * Public key of the coin.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Signature using the @e denom key over the @e coin_pub.
+ */
+ struct TALER_DenominationSignature ub_sig;
+
+ /**
+ * Signature of the coin's private key over the contract.
+ */
+ struct TALER_CoinSpendSignatureP coin_sig;
+
+ /**
+ * Offset of this coin into the `dc` array of all coins in the
+ * @e pc.
+ */
+ unsigned int index;
+
+};
/**
@@ -103,7 +108,7 @@ struct PayContext
struct TM_HandlerContext hc;
/**
- * Pointer to the global (malloc'd) array of all coins outcomes
+ * Array with @e coins_cnt coins we are despositing.
*/
struct MERCHANT_DepositConfirmation *dc;
@@ -113,146 +118,452 @@ struct PayContext
struct MHD_Connection *connection;
/**
+ * Handle to the mint that we are doing the payment with.
+ * (initially NULL while @e fo is trying to find a mint).
+ */
+ struct TALER_MINT_Handle *mh;
+
+ /**
+ * Handle for operation to lookup /keys (and auditors) from
+ * the mint used for this transaction; NULL if no operation is
+ * pending.
+ */
+ struct TMH_MINTS_FindOperation *fo;
+
+ /**
* Placeholder for #TMH_PARSE_post_json() to keep its internal state.
*/
void *json_parse_context;
/**
- * Response to return, NULL if we don't have one yet.
+ * Mint URI given in @e root.
*/
- struct MHD_Response *response;
+ char *chosen_mint;
/**
- * Transaction id
+ * Transaction ID given in @e root.
*/
uint64_t transaction_id;
/**
- * How many coins this paymen is made of.
+ * Maximum fee the merchant is willing to pay, from @e root.
+ * Note that IF the total fee of the mint is higher, that is
+ * acceptable to the merchant if the customer is willing to
+ * pay the difference (i.e. amount - max_fee <= actual-amount - actual-fee).
+ */
+ struct TALER_Amount max_fee;
+
+ /**
+ * Amount from @e root. This is the amount the merchant expects
+ * to make, minus @e max_fee.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Timestamp from @e root.
+ */
+ struct GNUNET_TIME_Absolute timestamp;
+
+ /**
+ * Refund deadline from @e root.
+ */
+ struct GNUNET_TIME_Absolute refund_deadline;
+
+ /**
+ * "H_contract" from @e root.
+ */
+ struct GNUNET_HashCode h_contract;
+
+ /**
+ * Execution date. How soon would the merchant like the
+ * transaction to be executed? (Can be given by the frontend
+ * or be determined by our configuration via #edate_delay.)
+ */
+ struct GNUNET_TIME_Absolute edate;
+
+ /**
+ * Response to return, NULL if we don't have one yet.
+ */
+ struct MHD_Response *response;
+
+ /**
+ * Number of coins this payment is made of. Length
+ * of the @e dc array.
*/
unsigned int coins_cnt;
/**
+ * Number of transactions still pending. Initially set to
+ * @e coins_cnt, decremented on each transaction that
+ * successfully finished.
+ */
+ unsigned int pending;
+
+ /**
* HTTP status code to use for the reply, i.e 200 for "OK".
* Special value UINT_MAX is used to indicate hard errors
- * (no reply, return MHD_NO).
+ * (no reply, return #MHD_NO).
*/
unsigned int response_code;
};
+
/**
- * Information needed by a single /deposit callback to refer to its
- * own coin inside the confirmations array, namely `struct MERCHANT_DepositConfirmation *dc`
- * above. Note: this information can NOT be shared between all the callbacks.
+ * Resume the given pay context and send the given response.
+ * Stores the response in the @a pc and signals MHD to resume
+ * the connection. Also ensures MHD runs immediately.
+ *
+ * @param pc payment context
+ * @param response_code response code to use
+ * @param response response data to send back
*/
-struct DepositCallbackContext
+static void
+resume_pay_with_response (struct PayContext *pc,
+ unsigned int response_code,
+ struct MHD_Response *response)
{
+ pc->response_code = response_code;
+ pc->response = response;
+ MHD_resume_connection (pc->connection);
+ TMH_trigger_daemon (); /* we resumed, kick MHD */
+}
- /**
- * Offset of this coin into the array of all coins outcomes
- */
- unsigned int index;
- /**
- * Reference to the main PayContext
- */
- struct PayContext *pc;
+/**
+ * Abort all pending /deposit operations.
+ *
+ * @param pc pay context to abort
+ */
+static void
+abort_deposit (struct PayContext *pc)
+{
+ unsigned int i;
+
+ for (i=0;i<pc->coins_cnt;i++)
+ {
+ struct MERCHANT_DepositConfirmation *dci = &pc->dc[i];
+
+ if (NULL != dci->dh)
+ {
+ TALER_MINT_deposit_cancel (dci->dh);
+ dci->dh = NULL;
+ }
+ }
+}
-};
/**
* Callback to handle a deposit permission's response.
*
- * @param cls see `struct MERCHANT_DepositConfirmationCls` (i.e. a poinetr to the global
- * array of confirmations and an index for this call in that array). That way, the last
- * executed callback can detect that no other confirmations are on the way, and can pack
- * a response for the wallet
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit;
- * 0 if the mint's reply is bogus (fails to follow the protocol)
- * @param proof the received JSON reply, should be kept as proof (and, in case of errors,
- * be forwarded to the customer)
+ * @param cls a `struct MERCHANT_DepositConfirmation` (i.e. a pointer
+ * into the global array of confirmations and an index for this call
+ * in that array). That way, the last executed callback can detect
+ * that no other confirmations are on the way, and can pack a response
+ * for the wallet
+ * @param http_status HTTP response code, #MHD_HTTP_OK
+ * (200) for successful deposit; 0 if the mint's reply is bogus (fails
+ * to follow the protocol)
+ * @param proof the received JSON reply,
+ * should be kept as proof (and, in case of errors, be forwarded to
+ * the customer)
*/
static void
deposit_cb (void *cls,
unsigned int http_status,
json_t *proof)
{
- struct DepositCallbackContext *dcc = cls;
- int i;
-
- /*FIXME the index is the same for every individual cb */
- if (GNUNET_SYSERR ==
- MERCHANT_DB_update_deposit_permission (db_conn,
- dcc->pc->transaction_id,
- 0))
- /* TODO */
- printf ("db error\n");
- dcc->pc->dc[dcc->index].ackd = 1;
- dcc->pc->dc[dcc->index].exit_status = http_status;
- dcc->pc->dc[dcc->index].proof = proof;
-
- /* loop through the confirmation array and return accordingly */
- for (i = 0; i < dcc->pc->coins_cnt; i++)
+ struct MERCHANT_DepositConfirmation *dc = cls;
+ struct PayContext *pc = dc->pc;
+
+ dc->dh = NULL;
+ pc->pending--;
+ if (MHD_HTTP_OK != http_status)
{
- /* just return if there is at least one coin to be still
- confirmed */
- if (! dcc->pc->dc[i].ackd)
- {
- printf ("still vacant coins\n");
- return;
- }
+ /* Transaction failed; stop all other ongoing deposits */
+ abort_deposit (pc);
+ /* Forward error including 'proof' for the body */
+ resume_pay_with_response (pc,
+ http_status,
+ TMH_RESPONSE_make_json (proof));
+ return;
}
-
- printf ("All /deposit(s) ack'd\n");
-
- dcc->pc->response = MHD_create_response_from_buffer (strlen ("All coins ack'd by the mint\n"),
- "All coins ack'd by the mint\n",
- MHD_RESPMEM_MUST_COPY);
- dcc->pc->response_code = MHD_HTTP_OK;
- /* Clean up what we can already */
- MHD_resume_connection (dcc->pc->connection);
- TM_trigger_daemon (); /* we resumed, kick MHD */
+ /* store result to DB */
+ if (GNUNET_OK !=
+ db->store_payment (db->cls,
+ &pc->h_contract,
+ &h_wire,
+ pc->transaction_id,
+ pc->timestamp,
+ pc->refund_deadline,
+ &dc->amount_without_fee,
+ &dc->coin_pub,
+ proof))
+ {
+ /* internal error */
+ abort_deposit (pc);
+ /* Forward error including 'proof' for the body */
+ resume_pay_with_response (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_internal_error ("Merchant database error"));
+ return;
+ }
+ if (0 != pc->pending)
+ return; /* still more to do */
+ resume_pay_with_response (pc,
+ MHD_HTTP_OK,
+ MHD_create_response_from_buffer (0,
+ NULL,
+ MHD_RESPMEM_PERSISTENT));
}
+/**
+ * Custom cleanup routine for a `struct PayContext`.
+ *
+ * @param hc the `struct PayContext` to clean up.
+ */
static void
pay_context_cleanup (struct TM_HandlerContext *hc)
{
- int i;
struct PayContext *pc = (struct PayContext *) hc;
-
- for (i = 0; i < pc->coins_cnt; i++)
- GNUNET_free_non_null (pc->dc[i].dcc);
+ unsigned int i;
TMH_PARSE_post_cleanup_callback (pc->json_parse_context);
+ for (i=0;i<pc->coins_cnt;i++)
+ {
+ struct MERCHANT_DepositConfirmation *dc = &pc->dc[i];
- #if 0
+ if (NULL != dc->dh)
+ {
+ TALER_MINT_deposit_cancel (dc->dh);
+ dc->dh = NULL;
+ }
+ if (NULL != dc->denom.rsa_public_key)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free (dc->denom.rsa_public_key);
+ dc->denom.rsa_public_key = NULL;
+ }
+ if (NULL != dc->ub_sig.rsa_signature)
+ {
+ GNUNET_CRYPTO_rsa_signature_free (dc->ub_sig.rsa_signature);
+ dc->ub_sig.rsa_signature = NULL;
+ }
+ }
+ GNUNET_free_non_null (pc->dc);
+ if (NULL != pc->fo)
+ {
+ TMH_MINTS_find_mint_cancel (pc->fo);
+ pc->fo = NULL;
+ }
if (NULL != pc->response)
{
- /* undestroyable regardless of the other MHD_destroy_response called
- in this source, FIXME */
-
MHD_destroy_response (pc->response);
pc->response = NULL;
}
- #endif
-
- GNUNET_free_non_null (pc->dc);
GNUNET_free (pc);
}
/**
+ * Function called with the result of our mint lookup.
+ *
+ * @param cls the `struct PayContext`
+ * @param mh NULL if mint was not found to be acceptable
+ * @param mint_trusted #GNUNET_YES if this mint is trusted by config
+ */
+static void
+process_pay_with_mint (void *cls,
+ struct TALER_MINT_Handle *mh,
+ int mint_trusted)
+{
+ struct PayContext *pc = cls;
+ struct TALER_Amount acc_fee;
+ struct TALER_Amount acc_amount;
+ const struct TALER_MINT_Keys *keys;
+ unsigned int i;
+
+ pc->fo = NULL;
+ if (NULL == mh)
+ {
+ /* The mint on offer is not in the set of our (trusted)
+ mints. Reject the payment. */
+ GNUNET_break_op (0);
+ resume_pay_with_response (pc,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TMH_RESPONSE_make_external_error ("mint not supported"));
+ return;
+ }
+ pc->mh = mh;
+
+ keys = TALER_MINT_get_keys (mh);
+ if (NULL == keys)
+ {
+ GNUNET_break (0);
+ resume_pay_with_response (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_internal_error ("no keys"));
+ return;
+ }
+
+ /* Total up the fees and the value of the deposited coins! */
+ for (i=0;i<pc->coins_cnt;i++)
+ {
+ struct MERCHANT_DepositConfirmation *dc = &pc->dc[i];
+ const struct TALER_MINT_DenomPublicKey *denom_details;
+
+ denom_details = TALER_MINT_get_denomination_key (keys,
+ &dc->denom);
+ if (NULL == denom_details)
+ {
+ resume_pay_with_response (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TMH_RESPONSE_make_json_pack ("{s:s, s:o}",
+ "hint", "unknown denom to mint",
+ "denom_pub", TALER_json_from_rsa_public_key (dc->denom.rsa_public_key)));
+ return;
+ }
+ if (GNUNET_OK !=
+ TMH_AUDITORS_check_dk (mh,
+ denom_details,
+ mint_trusted))
+ {
+ resume_pay_with_response (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TMH_RESPONSE_make_json_pack ("{s:s, s:o}",
+ "hint", "no acceptable auditor for denomination",
+ "denom_pub", TALER_json_from_rsa_public_key (dc->denom.rsa_public_key)));
+ return;
+ }
+ if (0 == i)
+ {
+ acc_fee = denom_details->fee_deposit;
+ acc_amount = dc->percoin_amount;
+ }
+ else
+ {
+ if ( (GNUNET_OK !=
+ TALER_amount_add (&acc_fee,
+ &denom_details->fee_deposit,
+ &acc_fee)) ||
+ (GNUNET_OK !=
+ TALER_amount_add (&acc_amount,
+ &dc->percoin_amount,
+ &acc_amount)) )
+ {
+ GNUNET_break_op (0);
+ /* Overflow in these amounts? Very strange. */
+ resume_pay_with_response (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TMH_RESPONSE_make_internal_error ("Overflow adding up amounts"));
+ return;
+ }
+ }
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&dc->amount_without_fee,
+ &dc->percoin_amount,
+ &denom_details->fee_deposit))
+ {
+ GNUNET_break_op (0);
+ /* fee higher than residual coin value, makes no sense. */
+ resume_pay_with_response (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TMH_RESPONSE_make_internal_error ("Fee higher than coin value"));
+ return;
+ }
+ }
+
+ /* Now check that the customer paid enough for the full contract */
+ if (-1 == TALER_amount_cmp (&pc->max_fee,
+ &acc_fee))
+ {
+ /* acc_fee > max_fee, customer needs to cover difference */
+ struct TALER_Amount excess_fee;
+ struct TALER_Amount total_needed;
+
+ /* compute fee amount to be covered by customer */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_subtract (&excess_fee,
+ &acc_fee,
+ &pc->max_fee));
+ /* add that to the total */
+ if (GNUNET_OK !=
+ TALER_amount_add (&total_needed,
+ &excess_fee,
+ &pc->amount))
+ {
+ GNUNET_break (0);
+ resume_pay_with_response (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_internal_error ("overflow"));
+ return;
+ }
+ /* check if total payment sufficies */
+ if (-1 == TALER_amount_cmp (&acc_amount,
+ &total_needed))
+ {
+ resume_pay_with_response (pc,
+ MHD_HTTP_NOT_ACCEPTABLE,
+ TMH_RESPONSE_make_external_error ("insufficient funds (including excessive mint fees to be covered by customer)"));
+ return;
+ }
+ }
+ else
+ {
+ /* fees are acceptable, we cover them all; let's check the amount */
+ if (-1 == TALER_amount_cmp (&acc_amount,
+ &pc->amount))
+ {
+ resume_pay_with_response (pc,
+ MHD_HTTP_NOT_ACCEPTABLE,
+ TMH_RESPONSE_make_external_error ("insufficient funds"));
+ return;
+ }
+ }
+
+ /* Initiate /deposit operation for all coins */
+ for (i=0;i<pc->coins_cnt;i++)
+ {
+ struct MERCHANT_DepositConfirmation *dc = &pc->dc[i];
+
+ dc->dh = TALER_MINT_deposit (mh,
+ &dc->percoin_amount,
+ pc->edate,
+ j_wire,
+ &pc->h_contract,
+ &dc->coin_pub,
+ &dc->ub_sig,
+ &dc->denom,
+ pc->timestamp,
+ pc->transaction_id,
+ &pubkey,
+ pc->refund_deadline,
+ &dc->coin_sig,
+ &deposit_cb,
+ dc);
+ if (NULL == dc->dh)
+ {
+ resume_pay_with_response (pc,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ TMH_RESPONSE_make_json_pack ("{s:s, s:i}",
+ "mint", pc->chosen_mint,
+ "transaction_id", pc->transaction_id));
+ return;
+ }
+ }
+}
+
+
+/**
* Accomplish this payment.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure
- * (can be updated)
+ * (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a
- * upload_data
+ * upload_data
* @return MHD result code
*/
int
@@ -263,53 +574,8 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
size_t *upload_data_size)
{
struct PayContext *pc;
- json_t *root;
- json_t *coins;
- char *chosen_mint;
- json_t *coin_aggregate;
- json_t *wire_details;
- unsigned int mint_index; /*a cell in the global array*/
- unsigned int coins_index;
- unsigned int coins_cnt;
- uint64_t transaction_id;
int res;
- struct TALER_MINT_DepositHandle *dh;
- struct TALER_Amount max_fee;
- struct TALER_Amount acc_fee;
- struct TALER_Amount coin_fee;
- struct TALER_Amount amount;
- struct TALER_Amount percoin_amount;
- struct GNUNET_TIME_Absolute edate;
- struct GNUNET_TIME_Absolute timestamp;
- struct GNUNET_TIME_Absolute refund_deadline;
- struct TALER_MerchantPublicKeyP pubkey;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct TALER_DenominationPublicKey denom_pub;
- struct TALER_DenominationSignature ub_sig;
- struct TALER_CoinSpendSignatureP coin_sig;
- struct GNUNET_HashCode h_contract;
- struct MERCHANT_DepositConfirmation *dc;
-
- struct TMH_PARSE_FieldSpecification spec[] = {
- TMH_PARSE_member_array ("coins", &coins),
- TMH_PARSE_member_string ("mint", &chosen_mint),
- TMH_PARSE_member_amount ("max_fee", &max_fee),
- TMH_PARSE_member_amount ("amount", &amount),
- TMH_PARSE_member_time_abs ("timestamp", &timestamp),
- TMH_PARSE_member_time_abs ("refund_deadline", &refund_deadline),
- TMH_PARSE_member_uint64 ("transaction_id", &transaction_id),
- TMH_PARSE_member_fixed ("H_contract", &h_contract),
- TMH_PARSE_MEMBER_END
- };
-
- struct TMH_PARSE_FieldSpecification coin_aggregate_spec[] = {
- TMH_PARSE_member_amount ("f", &percoin_amount),
- TMH_PARSE_member_fixed ("coin_pub", &coin_pub.eddsa_pub),
- TMH_PARSE_member_denomination_public_key ("denom_pub", &denom_pub),
- TMH_PARSE_member_denomination_signature ("ub_sig", &ub_sig),
- TMH_PARSE_member_fixed ("coin_sig", &coin_sig.eddsa_signature),
- TMH_PARSE_MEMBER_END
- };
+ json_t *root;
if (NULL == *connection_cls)
{
@@ -325,21 +591,17 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
}
if (0 != pc->response_code)
{
+ /* We are *done* processing the request, just queue the response (!) */
if (UINT_MAX == pc->response_code)
return MHD_NO; /* hard error */
- /* We are *done* processing the request, just queue the response (!) */
res = MHD_queue_response (connection,
pc->response_code,
pc->response);
- #if 0
- if (pc->response != NULL)
+ if (NULL != pc->response)
{
- /* undestroyable regardless of the other MHD_destroy_response called
- in this source, FIXME */
MHD_destroy_response (pc->response);
pc->response = NULL;
}
- #endif
return res;
}
@@ -349,201 +611,109 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
upload_data_size,
&root);
if (GNUNET_SYSERR == res)
- return MHD_NO;
- /* the POST's body has to be further fetched */
+ return MHD_NO; /* error parsing JSON */
if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES;
-
- res = TMH_PARSE_json_data (connection,
- root,
- spec);
-
- if (GNUNET_YES != res)
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-
- /* 1 Check if the chosen mint is among the merchant's preferred.
-
- An error in this case could be due to:
-
- * the wallet indicated a non existent mint
- * the wallet indicated a non trusted mint
+ return MHD_YES; /* the POST's body has to be further fetched */
- NOTE: by preventively checking this, the merchant
- avoids getting HTTP response codes from random
- websites that may mislead the wallet in the way
- of managing the error. Of course, that protect the
- merchant from POSTing coins to untrusted mints.
-
- */
-
- for (mint_index = 0; mint_index <= nmints; mint_index++)
+ /* Got the JSON upload, parse it */
{
- /* no mint found in array */
- if (mint_index == nmints)
+ json_t *coins;
+ json_t *coin;
+ unsigned int coins_index;
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_array ("coins", &coins),
+ TMH_PARSE_member_string ("mint", &pc->chosen_mint),
+ TMH_PARSE_member_uint64 ("transaction_id", &pc->transaction_id),
+ TMH_PARSE_member_amount ("max_fee", &pc->max_fee),
+ TMH_PARSE_member_amount ("amount", &pc->amount),
+ TMH_PARSE_member_time_abs ("timestamp", &pc->timestamp),
+ TMH_PARSE_member_time_abs ("refund_deadline", &pc->refund_deadline),
+ TMH_PARSE_member_fixed ("H_contract", &pc->h_contract),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_json_data (connection,
+ root,
+ spec);
+ if (GNUNET_YES != res)
{
- mint_index = -1;
- break;
+ json_decref (root);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
- /* test it by checking public key */
- if (0 == strcmp (mints[mint_index].hostname,
- chosen_mint))
- break;
-
- }
-
- if (-1 == mint_index)
- return TMH_RESPONSE_reply_external_error (connection, "unknown mint");
-
- /* no 'edate' from frontend. Generate it here; it will be timestamp
- + a edate delay supplied in config file */
- if (NULL == json_object_get (root, "edate"))
- {
- edate = GNUNET_TIME_absolute_add (timestamp, edate_delay);
- if (-1 == json_object_set (root, "edate", TALER_json_from_abs (edate)))
- return MHD_NO;
- }
-
- coins_cnt = json_array_size (coins);
-
- if (0 == coins_cnt)
- return TMH_RESPONSE_reply_external_error (connection, "no coins given");
-
- json_array_foreach (coins, coins_index, coin_aggregate)
- {
- res = deposit_fee_from_coin_aggregate (connection,
- coin_aggregate,
- &coin_fee,
- mint_index);
- if (GNUNET_NO == res)
- return MHD_YES;
- if (GNUNET_SYSERR == res)
- return MHD_NO;
-
- if (0 == coins_index)
- acc_fee = coin_fee;
+ /* 'edate' is optional, if it is not present, generate it here; it
+ will be timestamp plus the edate_delay supplied in config
+ file */
+ if (NULL == json_object_get (root, "edate"))
+ {
+ pc->edate = GNUNET_TIME_absolute_add (pc->timestamp,
+ edate_delay);
+ }
else
- TALER_amount_add (&acc_fee,
- &acc_fee,
- &coin_fee);
- }
-
-
- if (-1 == TALER_amount_cmp (&max_fee, &acc_fee))
- return MHD_HTTP_NOT_ACCEPTABLE;
-
- /* cutting off unneeded fields from deposit permission as
- gotten from the wallet */
- if (-1 == json_object_del (root, "mint"))
- return TMH_RESPONSE_reply_external_error (connection,
- "malformed/non-existent 'mint' field");
- if (-1 == json_object_del (root, "coins"))
- return TMH_RESPONSE_reply_external_error (connection,
- "malformed/non-existent 'coins' field");
-
- /* adding our public key to deposit permission */
- GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pubkey.eddsa_pub);
- json_object_set_new (root,
- "merchant_pub",
- TALER_json_from_data (&pubkey, sizeof (pubkey)));
-
- wire_details = MERCHANT_get_wire_json (wire, salt);
- /* since memory is zero'd out by GNUNET_malloc, any 'ackd' field will be
- (implicitly) set to false */
- dc = GNUNET_malloc (coins_cnt * sizeof (struct MERCHANT_DepositConfirmation));
- if (NULL == dc)
- return TMH_RESPONSE_reply_internal_error (connection, "memory failure");
-
- /* DEBUG CHECKPOINT: return a provisory fullfilment page to the wallet
- to test the reception of coins array */
-
- #ifdef COINSCHECKPOINT
- rh->data = "Coins received\n";
- return TMH_MHD_handler_static_response (rh,
- connection,
- connection_cls,
- upload_data,
- upload_data_size);
-
- #endif
-
- /* suspend connection until the last coin has been ack'd to the cb.
- That last cb will finally resume the connection and send back a response */
- MHD_suspend_connection (connection);
-
- pc->dc = dc;
- pc->coins_cnt = coins_cnt;
- pc->transaction_id = transaction_id;
+ {
+ struct TMH_PARSE_FieldSpecification espec[] = {
+ TMH_PARSE_member_time_abs ("edate", &pc->edate),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_json_data (connection,
+ root,
+ espec);
+ if (GNUNET_YES != res)
+ {
+ json_decref (root);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ }
- json_array_foreach (coins, coins_index, coin_aggregate)
- {
+ pc->coins_cnt = json_array_size (coins);
+ if (0 == pc->coins_cnt)
+ {
+ json_decref (root);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "no coins given");
+ }
+ /* note: 1 coin = 1 deposit confirmation expected */
+ pc->dc = GNUNET_new_array (pc->coins_cnt,
+ struct MERCHANT_DepositConfirmation);
- /* 3 For each coin in DB
-
- a. Generate a deposit permission
- b. store it in DB
- c. POST to the mint (see mint-lib for this)
- (retry until getting a persisten state)
- */
-
- /* a */
- if (-1 == json_object_update (root, coin_aggregate))
- return TMH_RESPONSE_reply_internal_error (connection,
- "deposit permission not generated for storing");
-
- /* b */
- char *deposit_permission_str = json_dumps (root, JSON_COMPACT);
- if (GNUNET_OK != MERCHANT_DB_store_deposit_permission (db_conn,
- deposit_permission_str,
- transaction_id,
- 1,
- mints[mint_index].hostname))
- return TMH_RESPONSE_reply_internal_error (connection, "internal DB failure");
- res = TMH_PARSE_json_data (connection,
- coin_aggregate,
- coin_aggregate_spec);
- if (GNUNET_OK != res)
- return res; /* may return GNUNET_NO */
-
- /* c */
- struct DepositCallbackContext *percoin_dcc = GNUNET_new (struct DepositCallbackContext);
- pc->dc[coins_index].dcc = percoin_dcc;
- percoin_dcc->index = coins_index;
- percoin_dcc->pc = pc;
-
- dh = TALER_MINT_deposit (mints[mint_index].conn,
- &percoin_amount,
- edate,
- wire_details,
- &h_contract,
- &coin_pub,
- &ub_sig,
- &denom_pub,
- timestamp,
- transaction_id,
- &pubkey,
- refund_deadline,
- &coin_sig,
- &deposit_cb,
- percoin_dcc); /*FIXME TODO instantiate an individual cls for each
- cb: each of them needs an index which points the
- array of all the confirmations */
- if (NULL == dh)
+ json_array_foreach (coins, coins_index, coin)
{
- MHD_resume_connection (connection);
- return TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_SERVICE_UNAVAILABLE,
- "{s:s, s:i}",
- "mint", mints[mint_index].hostname,
- "transaction_id", transaction_id);
+ struct MERCHANT_DepositConfirmation *dc = &pc->dc[coins_index];
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_denomination_public_key ("denom_pub", &dc->denom),
+ TMH_PARSE_member_amount ("f", &dc->percoin_amount),
+ TMH_PARSE_member_fixed ("coin_pub", &dc->coin_pub),
+ TMH_PARSE_member_denomination_signature ("ub_sig", &dc->ub_sig),
+ TMH_PARSE_member_fixed ("coin_sig", &dc->coin_sig),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_json_data (connection,
+ coin,
+ spec);
+ if (GNUNET_YES != res)
+ {
+ json_decref (root);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ dc->index = coins_index;
+ dc->pc = pc;
}
}
- GNUNET_SCHEDULER_cancel (poller_task);
- GNUNET_SCHEDULER_add_now (context_task, mints[mint_index].ctx);
- return MHD_YES;
+ /* Find the responsible mint, this may take a while... */
+ pc->pending = pc->coins_cnt;
+ pc->fo = TMH_MINTS_find_mint (pc->chosen_mint,
+ &process_pay_with_mint,
+ pc);
- /* 4 Return response code: success, or whatever data the
- mint sent back regarding some bad coin */
+ /* ... so we suspend connection until the last coin has been ack'd
+ or until we have encountered a hard error. Eventually, we will
+ resume the connection and send back a response using
+ #resume_pay_with_response(). */
+ MHD_suspend_connection (connection);
+ json_decref (root);
+ return MHD_YES;
}
diff --git a/src/backend/taler-merchant-httpd_pay.h b/src/backend/taler-merchant-httpd_pay.h
index 6a796e06..0836f9ba 100644
--- a/src/backend/taler-merchant-httpd_pay.h
+++ b/src/backend/taler-merchant-httpd_pay.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -13,17 +13,16 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
-
/**
- * @file merchant/backend/taler-merchant-httpd_contract.h
+ * @file backend/taler-merchant-httpd_pay.h
* @brief headers for /pay handler
* @author Marcello Stanisci
*/
-
#ifndef TALER_MINT_HTTPD_PAY_H
#define TALER_MINT_HTTPD_PAY_H
#include <microhttpd.h>
-#include "taler-mint-httpd.h"
+#include "taler-merchant-httpd.h"
+
/**
* Manage a payment
@@ -33,7 +32,6 @@
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- *
* @return MHD result code
*/
int
diff --git a/src/backend/taler-mint-httpd_responses.c b/src/backend/taler-merchant-httpd_responses.c
index 00a4d25f..d8ba1170 100644
--- a/src/backend/taler-mint-httpd_responses.c
+++ b/src/backend/taler-merchant-httpd_responses.c
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file taler-mint-httpd_responses.c
+ * @file taler-merchant-httpd_responses.c
* @brief API for generating the various replies of the mint; these
* functions are called TMH_RESPONSE_reply_ and they generate
* and queue MHD response objects for a given connection.
@@ -23,27 +23,22 @@
* @author Christian Grothoff
*/
#include "platform.h"
-#include "taler-mint-httpd_responses.h"
+#include "taler-merchant-httpd_responses.h"
#include <taler/taler_util.h>
#include <gnunet/gnunet_util_lib.h>
/**
- * Send JSON object as response.
+ * Make JSON response object.
*
- * @param connection the MHD connection
* @param json the json object
- * @param response_code the http response code
- * @return MHD result code
+ * @return MHD response object
*/
-int
-TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
- const json_t *json,
- unsigned int response_code)
+struct MHD_Response *
+TMH_RESPONSE_make_json (const json_t *json)
{
struct MHD_Response *resp;
char *json_str;
- int ret;
json_str = json_dumps (json, JSON_INDENT(2));
GNUNET_assert (NULL != json_str);
@@ -53,11 +48,34 @@ TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
{
free (json_str);
GNUNET_break (0);
- return MHD_NO;
+ return NULL;
}
(void) MHD_add_response_header (resp,
MHD_HTTP_HEADER_CONTENT_TYPE,
"application/json");
+ return resp;
+}
+
+
+/**
+ * Send JSON object as response.
+ *
+ * @param connection the MHD connection
+ * @param json the json object
+ * @param response_code the http response code
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
+ const json_t *json,
+ unsigned int response_code)
+{
+ struct MHD_Response *resp;
+ int ret;
+
+ resp = TMH_RESPONSE_make_json (json);
+ if (NULL == resp)
+ return MHD_NO;
ret = MHD_queue_response (connection,
response_code,
resp);
@@ -67,6 +85,40 @@ TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
/**
+ * Make JSON response object.
+ *
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_json_pack (const char *fmt,
+ ...)
+{
+ json_t *json;
+ va_list argp;
+ struct MHD_Response *ret;
+ json_error_t jerror;
+
+ va_start (argp, fmt);
+ json = json_vpack_ex (&jerror, 0, fmt, argp);
+ va_end (argp);
+ if (NULL == json)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to pack JSON with format `%s': %s\n",
+ fmt,
+ jerror.text);
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ ret = TMH_RESPONSE_make_json (json);
+ json_decref (json);
+ return ret;
+}
+
+
+/**
* Function to call to handle the request by building a JSON
* reply from a format string and varargs.
*
@@ -106,6 +158,22 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection *connection,
return ret;
}
+
+/**
+ * Create a response indicating an internal error.
+ *
+ * @param hint hint about the internal error's nature
+ * @return a MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_internal_error (const char *hint)
+{
+ return TMH_RESPONSE_make_json_pack ("{s:s, s:s}",
+ "error", "internal error",
+ "hint", hint);
+}
+
+
/**
* Send a response indicating an internal error.
*
@@ -124,6 +192,7 @@ TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
"hint", hint);
}
+
/**
* Send a response indicating that the request was too big.
*
@@ -186,6 +255,7 @@ TMH_RESPONSE_add_global_headers (struct MHD_Response *response)
"close");
}
+
/**
* Send a response indicating an external error.
*
@@ -203,4 +273,21 @@ TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
"error", "client error",
"hint", hint);
}
+
+
+/**
+ * Create a response indicating an external error.
+ *
+ * @param hint hint about the internal error's nature
+ * @return a MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_external_error (const char *hint)
+{
+ return TMH_RESPONSE_make_json_pack ("{s:s, s:s}",
+ "error", "client error",
+ "hint", hint);
+}
+
+
/* end of taler-mint-httpd_responses.c */
diff --git a/src/backend/taler-mint-httpd_responses.h b/src/backend/taler-merchant-httpd_responses.h
index f947bd57..7240d601 100644
--- a/src/backend/taler-mint-httpd_responses.h
+++ b/src/backend/taler-merchant-httpd_responses.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014 GNUnet e.V.
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -15,7 +15,7 @@
*/
/**
- * @file taler-mint-httpd_responses.h
+ * @file taler-merchant-httpd_responses.h
* @brief API for generating the various replies of the mint; these
* functions are called TMH_RESPONSE_reply_ and they generate
* and queue MHD response objects for a given connection.
@@ -31,6 +31,16 @@
#include <pthread.h>
/**
+ * Make JSON response object.
+ *
+ * @param json the json object
+ * @return MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_json (const json_t *json);
+
+
+/**
* Send JSON object as response.
*
* @param connection the MHD connection
@@ -45,6 +55,18 @@ TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
/**
+ * Make JSON response object.
+ *
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_json_pack (const char *fmt,
+ ...);
+
+
+/**
* Function to call to handle the request by building a JSON
* reply from a format string and varargs.
*
@@ -70,6 +92,7 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection *connection,
int
TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection);
+
/**
* Send a response indicating an internal error.
*
@@ -80,6 +103,18 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection);
int
TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
const char *hint);
+
+
+/**
+ * Create a response indicating an internal error.
+ *
+ * @param hint hint about the internal error's nature
+ * @return a MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_internal_error (const char *hint);
+
+
/**
* Send a response indicating an external error.
*
@@ -90,6 +125,18 @@ TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
int
TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
const char *hint);
+
+
+/**
+ * Create a response indicating an external error.
+ *
+ * @param hint hint about the internal error's nature
+ * @return a MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_external_error (const char *hint);
+
+
/**
* Send a response indicating that the request was too big.
*
@@ -99,6 +146,7 @@ TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
int
TMH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection);
+
/**
* Add headers we want to return in every response.
* Useful for testing, like if we want to always close
diff --git a/src/backend/taler-mint-httpd.h b/src/backend/taler-mint-httpd.h
deleted file mode 100644
index ad8702f0..00000000
--- a/src/backend/taler-mint-httpd.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-mint-httpd.h
- * @brief Global declarations for the mint
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- *
- * FIXME: Consider which of these need to really be globals...
- */
-#ifndef TALER_MINT_HTTPD_H
-#define TALER_MINT_HTTPD_H
-
-#include <microhttpd.h>
-
-/**
- * @brief Struct describing an URL and the handler for it.
- */
-struct TMH_RequestHandler
-{
-
- /**
- * URL the handler is for.
- */
- const char *url;
-
- /**
- * Method the handler is for, NULL for "all".
- */
- const char *method;
-
- /**
- * Mime type to use in reply (hint, can be NULL).
- */
- const char *mime_type;
-
- /**
- * Raw data for the @e handler
- */
- const void *data;
-
- /**
- * Number of bytes in @e data, 0 for 0-terminated.
- */
- size_t data_size;
-
- /**
- * Function to call to handle the request.
- *
- * @param rh this struct
- * @param mime_type the @e mime_type for the reply (hint, can be NULL)
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
- int (*handler)(struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
-
- /**
- * Default response code.
- */
- int response_code;
-};
-
-
-#endif
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
new file mode 100644
index 00000000..e70ad206
--- /dev/null
+++ b/src/backenddb/Makefile.am
@@ -0,0 +1,39 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+plugindir = $(libdir)/taler
+
+if HAVE_POSTGRESQL
+if HAVE_TALERPQ
+plugin_LTLIBRARIES = \
+ libtaler_plugin_merchantdb_postgres.la
+endif
+endif
+
+lib_LTLIBRARIES = \
+ libtalermerchantdb.la
+
+libtalermerchantdb_la_SOURCES = \
+ merchantdb_plugin.c
+
+libtalermerchantdb_la_LIBADD = \
+ $(LIBGCRYPT_LIBS) \
+ -ltalerutil \
+ -lgnunetutil
+
+libtalermerchantdb_la_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS) \
+ -version-info 0:0:0 \
+ -no-undefined
+
+libtaler_plugin_merchantdb_postgres_la_SOURCES = \
+ plugin_merchantdb_postgres.c
+libtaler_plugin_merchantdb_postgres_la_LIBADD = \
+ $(LTLIBINTL)
+libtaler_plugin_merchantdb_postgres_la_LDFLAGS = \
+ $(TALER_PLUGIN_LDFLAGS) \
+ -ltalerpq \
+ -ltalerutil \
+ -lpq \
+ -lgnunetpostgres \
+ -lgnunetutil $(XLIB)
diff --git a/src/backenddb/merchantdb_plugin.c b/src/backenddb/merchantdb_plugin.c
new file mode 100644
index 00000000..62a1a193
--- /dev/null
+++ b/src/backenddb/merchantdb_plugin.c
@@ -0,0 +1,150 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchantdb/merchantdb_plugin.c
+ * @brief Logic to load database plugin
+ * @author Christian Grothoff
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#include "platform.h"
+#include <taler/taler_util.h>
+#include "taler_merchantdb_plugin.h"
+#include <ltdl.h>
+
+
+/**
+ * Initialize the plugin.
+ *
+ * @param cfg configuration to use
+ * @return #GNUNET_OK on success
+ */
+struct TALER_MERCHANTDB_Plugin *
+TALER_MERCHANTDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ char *plugin_name;
+ char *lib_name;
+ struct GNUNET_CONFIGURATION_Handle *cfg_dup;
+ struct TALER_MERCHANTDB_Plugin *plugin;
+
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "merchant",
+ "db",
+ &plugin_name))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "merchant",
+ "db");
+ return NULL;
+ }
+ (void) GNUNET_asprintf (&lib_name,
+ "libtaler_plugin_merchantdb_%s",
+ plugin_name);
+ GNUNET_free (plugin_name);
+ cfg_dup = GNUNET_CONFIGURATION_dup (cfg);
+ plugin = GNUNET_PLUGIN_load (lib_name, cfg_dup);
+ if (NULL != plugin)
+ plugin->library_name = lib_name;
+ else
+ GNUNET_free (lib_name);
+ GNUNET_CONFIGURATION_destroy (cfg_dup);
+ return plugin;
+}
+
+
+/**
+ * Shutdown the plugin.
+ *
+ * @param plugin the plugin to unload
+ */
+void
+TALER_MERCHANTDB_plugin_unload (struct TALER_MERCHANTDB_Plugin *plugin)
+{
+ char *lib_name;
+
+ if (NULL == plugin)
+ return;
+ lib_name = plugin->library_name;
+ GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
+ plugin));
+ GNUNET_free (lib_name);
+}
+
+
+/**
+ * Libtool search path before we started.
+ */
+static char *old_dlsearchpath;
+
+
+/**
+ * Setup libtool paths.
+ */
+void __attribute__ ((constructor))
+plugin_init ()
+{
+ int err;
+ const char *opath;
+ char *path;
+ char *cpath;
+
+ err = lt_dlinit ();
+ if (err > 0)
+ {
+ FPRINTF (stderr,
+ _("Initialization of plugin mechanism failed: %s!\n"),
+ lt_dlerror ());
+ return;
+ }
+ opath = lt_dlgetsearchpath ();
+ if (NULL != opath)
+ old_dlsearchpath = GNUNET_strdup (opath);
+ path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
+ if (NULL != path)
+ {
+ if (NULL != opath)
+ {
+ GNUNET_asprintf (&cpath, "%s:%s", opath, path);
+ lt_dlsetsearchpath (cpath);
+ GNUNET_free (path);
+ GNUNET_free (cpath);
+ }
+ else
+ {
+ lt_dlsetsearchpath (path);
+ GNUNET_free (path);
+ }
+ }
+}
+
+
+/**
+ * Shutdown libtool.
+ */
+void __attribute__ ((destructor))
+plugin_fini ()
+{
+ lt_dlsetsearchpath (old_dlsearchpath);
+ if (NULL != old_dlsearchpath)
+ {
+ GNUNET_free (old_dlsearchpath);
+ old_dlsearchpath = NULL;
+ }
+ lt_dlexit ();
+}
+
+
+/* end of merchantdb_plugin.c */
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
new file mode 100644
index 00000000..2b4f41ec
--- /dev/null
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -0,0 +1,244 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant/plugin_merchantdb_postgres.c
+ * @brief database helper functions for postgres used by the merchant
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_postgres_lib.h>
+#include <taler/taler_util.h>
+#include <taler/taler_pq_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Type of the "cls" argument given to each of the functions in
+ * our API.
+ */
+struct PostgresClosure
+{
+
+ /**
+ * Postgres connection handle.
+ */
+ PGconn *conn;
+
+};
+
+
+#define PQSQL_strerror(kind, cmd, res) \
+ GNUNET_log_from (kind, "merchant-db", \
+ "SQL %s failed at %s:%u with error: %s", \
+ cmd, __FILE__, __LINE__, PQresultErrorMessage (res));
+
+
+/**
+ * Initialize merchant tables
+ *
+ * @param cls closure our `struct Plugin`
+ * @param tmp #GNUNET_YES if the tables are to be made temporary i.e. their
+ * contents are dropped when the database connection is closed
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+static int
+postgres_initialize (void *cls,
+ int tmp)
+{
+ struct PostgresClosure *pg = cls;
+ const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
+ char *sql;
+ PGresult *res;
+ ExecStatusType status;
+ int ret;
+
+ GNUNET_asprintf (&sql,
+ "CREATE %1$s TABLE IF NOT EXISTS payments ("
+ "h_contract BYTEA NOT NULL,"
+ "h_wire BYTEA NOT NULL,"
+ "transaction_id INT8 PRIMARY KEY,"
+ "timestamp INT8 NOT NULL,"
+ "refund_deadline INT8 NOT NULL,"
+ "amount_without_fee_val INT8 NOT NULL,"
+ "amount_without_fee_frac INT4 NOT NULL,"
+ "amount_without_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL,"
+ "coin_pub BYTEA NOT NULL,"
+ "mint_proof BYTEA NOT NULL);",
+ tmp_str);
+ ret = GNUNET_POSTGRES_exec (pg->conn,
+ sql);
+ GNUNET_free (sql);
+ if (GNUNET_OK != ret)
+ return ret;
+ if ( (NULL == (res = PQprepare (pg->conn,
+ "insert_payment",
+ "INSERT INTO payments"
+ "(h_contract"
+ ",h_wire"
+ ",transaction_id"
+ ",timestamp"
+ ",refund_deadline"
+ ",amount_without_fee_val"
+ ",amount_without_fee_frac"
+ ",amount_without_fee_curr"
+ ",coin_pub"
+ ",mint_proof) VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
+ 10, NULL))) ||
+ (PGRES_COMMAND_OK != (status = PQresultStatus(res))) )
+ {
+ if (NULL != res)
+ {
+ PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res);
+ PQclear (res);
+ }
+ return GNUNET_SYSERR;
+ }
+ PQclear (res);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Insert payment confirmation from the mint into the database.
+ *
+ * @param cls our plugin handle
+ * @param h_contract hash of the contract
+ * @param h_wire hash of our wire details
+ * @param transaction_id of the contract
+ * @param timestamp time of the confirmation
+ * @param refund refund deadline
+ * @param amount_without_fee amount the mint will deposit
+ * @param coin_pub public key of the coin
+ * @param mint_proof proof from the mint that coin was accepted
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error
+ */
+static int
+postgres_store_payment (void *cls,
+ const struct GNUNET_HashCode *h_contract,
+ const struct GNUNET_HashCode *h_wire,
+ uint64_t transaction_id,
+ struct GNUNET_TIME_Absolute timestamp,
+ struct GNUNET_TIME_Absolute refund,
+ const struct TALER_Amount *amount_without_fee,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ json_t *mint_proof)
+{
+ struct PostgresClosure *pg = cls;
+ PGresult *res;
+ ExecStatusType status;
+
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_auto_from_type (h_contract),
+ TALER_PQ_query_param_auto_from_type (h_wire),
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_absolute_time (&timestamp),
+ TALER_PQ_query_param_absolute_time (&refund),
+ TALER_PQ_query_param_amount (amount_without_fee),
+ TALER_PQ_query_param_auto_from_type (coin_pub),
+ TALER_PQ_query_param_json (mint_proof),
+ TALER_PQ_query_param_end
+ };
+
+ res = TALER_PQ_exec_prepared (pg->conn,
+ "insert_payment",
+ params);
+ status = PQresultStatus (res);
+
+ if (PGRES_COMMAND_OK != status)
+ {
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE);
+ if (NULL == sqlstate)
+ {
+ /* very unexpected... */
+ GNUNET_break (0);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+ /* 40P01: deadlock, 40001: serialization failure */
+ if ( (0 == strcmp (sqlstate,
+ "23505")))
+ {
+ /* Primary key violation */
+ PQclear (res);
+ return GNUNET_NO;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database commit failure: %s\n",
+ sqlstate);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (res);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Initialize Postgres database subsystem.
+ *
+ * @param cls a configuration instance
+ * @return NULL on error, otherwise a `struct TALER_MERCHANTDB_Plugin`
+ */
+void *
+libtaler_plugin_merchantdb_postgres_init (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct PostgresClosure *pg;
+ struct TALER_MERCHANTDB_Plugin *plugin;
+
+ pg = GNUNET_new (struct PostgresClosure);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_have_value (cfg,
+ "merchantdb-postgres",
+ "CONFIG"))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "merchantdb-postgres",
+ "CONFIG");
+ return NULL;
+ }
+ pg->conn = GNUNET_POSTGRES_connect (cfg, "merchantdb-postgres");
+ plugin = GNUNET_new (struct TALER_MERCHANTDB_Plugin);
+ plugin->cls = pg;
+ plugin->initialize = &postgres_initialize;
+ plugin->store_payment = &postgres_store_payment;
+
+ return plugin;
+}
+
+
+/**
+ * Shutdown Postgres database subsystem.
+ *
+ * @param cls a `struct TALER_MERCHANTDB_Plugin`
+ * @return NULL (always)
+ */
+void *
+libtaler_plugin_merchantdb_postgres_done (void *cls)
+{
+ struct TALER_MERCHANTDB_Plugin *plugin = cls;
+ struct PostgresClosure *pg = plugin->cls;
+
+ PQfinish (pg->conn);
+ GNUNET_free (pg);
+ GNUNET_free (plugin);
+ return NULL;
+}
diff --git a/src/frontend/fullfillment.php b/src/frontend/fullfillment.php
index d7677aa7..09de3fbf 100644
--- a/src/frontend/fullfillment.php
+++ b/src/frontend/fullfillment.php
@@ -27,6 +27,13 @@
$cli_debug = false;
$backend_test = true;
+function generate_msg ($link){
+ $msg = "Thanks for donating to " . $_SESSION['receiver'] . ".";
+ if (false != $link)
+ $msg .= " Check our latest <a href=\"" . $link . "\">news!</a>";
+ return $msg;
+}
+
if ($_GET['cli_debug'] == 'yes')
$cli_debug = true;
@@ -38,11 +45,23 @@ if ($_GET['backend_test'] == 'no')
session_start();
-
if (! isset ($_SESSION['payment_ok']))
echo "Please land here after a successful payment!";
-else
- echo "Thanks for donating to " . $_SESSION['receiver'];
+else{
+ $news = false;
+ switch ($_SESSION['receiver']){
+ case "Taler":
+ $news = "https://taler.net/news";
+ break;
+ case "GNUnet":
+ $news = "https://gnunet.org/";
+ break;
+ case "Tor":
+ $news = "https://www.torproject.org/press/press.html.en";
+ break;
+ }
+ echo generate_msg ($news);
+}
?>
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 3db59c88..f62655b3 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -1,2 +1,9 @@
+# This Makefile.am is in the public domain
EXTRA_DIST = \
- platform.h
+ platform.h
+
+talerincludedir = $(includedir)/taler
+
+talerinclude_HEADERS = \
+ taler_merchantdb_lib.h \
+ taler_merchant_service.h
diff --git a/src/include/merchant.h b/src/include/merchant.h
deleted file mode 100644
index eb430360..00000000
--- a/src/include/merchant.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file include/merchant.h
- * @brief Common utility functions for merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#ifndef MERCHANT_H
-#define MERCHANT_H
-
-#include <gnunet/gnunet_util_lib.h>
-#include <taler/taler_mint_service.h>
-#include "merchant.h"
-
-/**
- * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
- */
-#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000)
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-/**
- * Outcome of a /deposit request for a coin. Typically forming an array enclosed
- * into the unique PayContext
- */
-struct MERCHANT_DepositConfirmation
-{
- /**
- * Reference to the per-deposit-handler Context. Needed by the
- * cleanup function to get it freed
- */
- struct DepositCallbackContext *dcc;
-
- /**
- * True if this coin's outcome has been read from
- * its cb
- */
- unsigned int ackd;
-
- /**
- * The mint's response to this /deposit
- */
- unsigned int exit_status;
-
- /**
- * The mint's response body (JSON). Mainly useful in case
- * some callback needs to send back to the to the wallet the
- * outcome of an erroneous coin
- */
- json_t *proof;
-
-};
-
-
-/**
- * Mint
- */
-struct MERCHANT_Mint
-{
- /**
- * Hostname
- */
- char *hostname;
-
- /**
- * Flag which indicates whether some HTTP transfer between
- * this merchant and the mint is still ongoing
- */
- int pending;
-
- /**
- * A connection to this mint
- */
- struct TALER_MINT_Handle *conn;
-
- /**
- * This mint's context (useful to the event loop)
- */
- struct TALER_MINT_Context *ctx;
-
-};
-
-struct MERCHANT_Auditor
-{
- /**
- * Auditor's legal name
- */
- char *name;
-
-};
-
-/**
- * The contract sent by the merchant to the wallet
- */
-struct MERCHANT_Contract
-{
- /**
- * Purpose header for the signature over contract
- */
- struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
-
- /**
- * Hash of the JSON contract in UTF-8 including 0-termination,
- * using JSON_COMPACT | JSON_SORT_KEYS
- */
- struct GNUNET_HashCode h_contract;
-
-};
-
-
-
-/**
- * Parses mints from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of mints upon successful parsing. Will be NULL upon
- * error.
- * @return the number of mints in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_Mint **mints);
-
-/**
- * Parses auditors from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of auditors upon successful parsing. Will be NULL upon
- * error.
- * @return the number of auditors in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_auditors (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_Auditor **auditors);
-
-GNUNET_NETWORK_STRUCT_BEGIN
-struct MERCHANT_WIREFORMAT_Sepa
-{
- /**
- * The international bank account number
- */
- char *iban;
-
- /**
- * Name of the bank account holder
- */
- char *name;
-
- /**
- *The bank identification code
- */
- char *bic;
-
- /**
- * The latest payout date when the payment corresponding to this account has
- * to take place. A value of 0 indicates a transfer as soon as possible.
- */
- struct GNUNET_TIME_AbsoluteNBO payout;
-};
-GNUNET_NETWORK_STRUCT_END
-
-/**
- * Parse the SEPA information from the configuration. If any of the required
- * fileds is missing return NULL.
- *
- * @param cfg the configuration
- * @return Sepa details as a structure; NULL upon error
- */
-struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-
-/**
- * Destroy and free resouces occupied by the wireformat structure
- *
- * @param wf the wireformat structure
- */
-void
-TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf);
-
-#endif /* MERCHANT_H */
diff --git a/src/include/platform.h b/src/include/platform.h
index 4cba7abf..6e4baec6 100644
--- a/src/include/platform.h
+++ b/src/include/platform.h
@@ -18,7 +18,7 @@
* @file include/platform.h
* @brief This file contains the includes and definitions which are used by the
* rest of the modules
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
#ifndef PLATFORM_H_
@@ -28,7 +28,7 @@
#ifndef HAVE_USED_CONFIG_H
# define HAVE_USED_CONFIG_H
# ifdef HAVE_CONFIG_H
-# include "taler_config.h"
+# include "taler_merchant_config.h"
# endif
#endif
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h
new file mode 100644
index 00000000..8c9957c3
--- /dev/null
+++ b/src/include/taler_merchant_service.h
@@ -0,0 +1,292 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file include/taler_merchant_service.h
+ * @brief C interface of libtalermerchant, a C library to use merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#ifndef _TALER_MERCHANT_SERVICE_H
+#define _TALER_MERCHANT_SERVICE_H
+
+#include <taler/taler_util.h>
+
+/* ********************* event loop *********************** */
+
+/**
+ * @brief Handle to this library context. This is where the
+ * main event loop logic lives.
+ */
+struct TALER_MERCHANT_Context;
+
+
+/**
+ * Initialise a context. A context should be used for each thread and should
+ * not be shared among multiple threads.
+ *
+ * @return the context, NULL on error (failure to initialize)
+ */
+struct TALER_MERCHANT_Context *
+TALER_MERCHANT_init (void);
+
+
+/**
+ * Obtain the information for a select() call to wait until
+ * #TALER_MERCHANT_perform() is ready again. Note that calling
+ * any other TALER_MERCHANT-API may also imply that the library
+ * is again ready for #TALER_MERCHANT_perform().
+ *
+ * Basically, a client should use this API to prepare for select(),
+ * then block on select(), then call #TALER_MERCHANT_perform() and then
+ * start again until the work with the context is done.
+ *
+ * This function will NOT zero out the sets and assumes that @a max_fd
+ * and @a timeout are already set to minimal applicable values. It is
+ * safe to give this API FD-sets and @a max_fd and @a timeout that are
+ * already initialized to some other descriptors that need to go into
+ * the select() call.
+ *
+ * @param ctx context to get the event loop information for
+ * @param read_fd_set will be set for any pending read operations
+ * @param write_fd_set will be set for any pending write operations
+ * @param except_fd_set is here because curl_multi_fdset() has this argument
+ * @param max_fd set to the highest FD included in any set;
+ * if the existing sets have no FDs in it, the initial
+ * value should be "-1". (Note that `max_fd + 1` will need
+ * to be passed to select().)
+ * @param timeout set to the timeout in milliseconds (!); -1 means
+ * no timeout (NULL, blocking forever is OK), 0 means to
+ * proceed immediately with #TALER_MERCHANT_perform().
+ */
+void
+TALER_MERCHANT_get_select_info (struct TALER_MERCHANT_Context *ctx,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ fd_set *except_fd_set,
+ int *max_fd,
+ long *timeout);
+
+
+/**
+ * Run the main event loop for the Taler interaction.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_MERCHANT_perform (struct TALER_MERCHANT_Context *ctx);
+
+
+/**
+ * Cleanup library initialisation resources. This function should be called
+ * after using this library to cleanup the resources occupied during library's
+ * initialisation.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_MERCHANT_fini (struct TALER_MERCHANT_Context *ctx);
+
+
+/* ********************* /pay *********************** */
+
+
+/**
+ * @brief Handle to a /pay operation at a merchant. Note that we use
+ * the same handle for interactions with frontends (API for wallets)
+ * or backends (API for frontends). The difference is that for the
+ * frontend API, we need the private keys of the coins, while for
+ * the backend API we need the public keys and signatures received
+ * from the wallet. Also, the frontend returns a redirect URI on
+ * success, while the backend just returns a success status code.
+ */
+struct TALER_MERCHANT_Pay;
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * /pay request to a merchant.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, 200 or 300-level response codes
+ * can indicate success, depending on whether the interaction
+ * was with a merchant frontend or backend;
+ * 0 if the merchant's reply is bogus (fails to follow the protocol)
+ * @param redirect_uri URI for the redirect, if the request was successful and we were talking to a frontend;
+ * NULL if the request failed or if were were talking to a backend
+ * @param obj the received JSON reply, with error details if the request failed
+ */
+typedef void
+(*TALER_MERCHANT_PayCallback) (void *cls,
+ unsigned int http_status,
+ const char *redirect_uri,
+ json_t *obj);
+
+
+/**
+ * Information we need from the wallet for each coin when doing the
+ * payment.
+ */
+struct TALER_MERCHANT_PayCoin
+{
+ /**
+ * Denomination key with which the coin is signed
+ */
+ struct TALER_DenominationPublicKey denom_pub;
+
+ /**
+ * Mint’s unblinded signature of the coin
+ */
+ struct TALER_DenominationSignature denom_sig;
+
+ /**
+ * Coin's private key.
+ */
+ struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+ /**
+ * Amount this coin is to contribute (including fee).
+ */
+ struct TALER_Amount amount_with_fee;
+
+ /**
+ * Amount this coin is to contribute (without fee).
+ */
+ struct TALER_Amount amount_without_fee;
+};
+
+
+/**
+ * Pay a merchant. API for wallets that have the coin's private keys.
+ *
+ * @param merchant the merchant context
+ * @param mint_uri URI of the mint that the coins belong to
+ * @param h_wire hash of the merchant’s account details
+ * @param h_contract hash of the contact of the merchant with the customer
+ * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
+ * @param transaction_id transaction id for the transaction between merchant and customer
+ * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
+ * @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed)
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key.
+ * @param pay_cb the callback to call when a reply for this request is available
+ * @param pay_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_Pay *
+TALER_MERCHANT_pay_wallet (struct TALER_MERCHANT_Context *merchant,
+ const char *mint_uri,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ struct GNUNET_TIME_Absolute timestamp,
+ uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ struct GNUNET_TIME_Absolute refund_deadline,
+ unsigned int num_coins,
+ const struct TALER_MERCHANT_PayCoin *coins,
+ TALER_MERCHANT_PayCallback pay_cb,
+ void *pay_cb_cls);
+
+
+/**
+ * Information we need from the frontend when forwarding
+ * a payment to the backend.
+ */
+struct TALER_MERCHANT_PaidCoin
+{
+ /**
+ * Denomination key with which the coin is signed
+ */
+ struct TALER_DenominationPublicKey denom_pub;
+
+ /**
+ * Mint’s unblinded signature of the coin
+ */
+ struct TALER_DenominationSignature denom_sig;
+
+ /**
+ * Coin's public key.
+ */
+ struct TALER_CoinSpendPrivateKeyP coin_pub;
+
+ /**
+ * Coin's signature key.
+ */
+ struct TALER_CoinSpendSignatureP coin_sig;
+
+ /**
+ * Amount this coin is to contribute (including fee).
+ */
+ struct TALER_Amount amount_with_fee;
+
+ /**
+ * Amount this coin is to contribute (without fee).
+ */
+ struct TALER_Amount amount_without_fee;
+
+};
+
+
+/**
+ * Pay a merchant. API for frontends talking to backends. Here,
+ * the frontend does not have the coin's private keys, but just
+ * the public keys and signatures. Note the sublte difference
+ * in the type of @a coins compared to #TALER_MERCHANT_pay().
+ *
+ * @param merchant the merchant context
+ * @param mint_uri URI of the mint that the coins belong to
+ * @param h_wire hash of the merchant’s account details
+ * @param h_contract hash of the contact of the merchant with the customer
+ * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
+ * @param transaction_id transaction id for the transaction between merchant and customer
+ * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
+ * @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed)
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key.
+ * @param pay_cb the callback to call when a reply for this request is available
+ * @param pay_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_Pay *
+TALER_MERCHANT_pay_frontend (struct TALER_MERCHANT_Context *merchant,
+ const char *mint_uri,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ struct GNUNET_TIME_Absolute timestamp,
+ uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ struct GNUNET_TIME_Absolute refund_deadline,
+ unsigned int num_coins,
+ const struct TALER_MERCHANT_PaidCoin *coins,
+ TALER_MERCHANT_PayCallback pay_cb,
+ void *pay_cb_cls);
+
+
+/**
+ * Cancel a /pay request. Note that if you cancel a request like
+ * this, you have no assurance that the request has not yet been
+ * forwarded to the merchant. Thus, the payment may still succeed or
+ * fail. Re-issue the original /pay request to resume/retry and
+ * obtain a definitive result, or /refresh the coins involved to
+ * ensure that the merchant can no longer complete the payment.
+ *
+ * @param wh the wire information request handle
+ */
+void
+TALER_MERCHANT_pay_cancel (struct TALER_MERCHANT_Pay *ph);
+
+
+#endif /* _TALER_MERCHANT_SERVICE_H */
diff --git a/src/include/taler_merchantdb_lib.h b/src/include/taler_merchantdb_lib.h
new file mode 100644
index 00000000..f64a9bfa
--- /dev/null
+++ b/src/include/taler_merchantdb_lib.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_merchantdb_lib.h
+ * @brief database helper functions used by the merchant backend
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#ifndef TALER_MERCHANTDB_LIB_H
+#define TALER_MERCHANTDB_LIB_H
+
+#include <taler/taler_util.h>
+#include "taler_merchantdb_plugin.h"
+
+/**
+ * Handle to interact with the database.
+ */
+struct TALER_MERCHANTDB_Plugin;
+
+/**
+ * Connect to postgresql database
+ *
+ * @param cfg the configuration handle
+ * @return connection to the database; NULL upon error
+ */
+struct TALER_MERCHANTDB_Plugin *
+TALER_MERCHANTDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Disconnect from the database
+ *
+ * @param dbh database handle to close
+ */
+void
+TALER_MERCHANTDB_plugin_unload (struct TALER_MERCHANTDB_Plugin *dbh);
+
+
+#endif /* MERCHANT_DB_H */
+
+/* end of taler_merchantdb_lib.h */
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
new file mode 100644
index 00000000..f237b8d7
--- /dev/null
+++ b/src/include/taler_merchantdb_plugin.h
@@ -0,0 +1,88 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file include/taler_merchantdb_plugin.h
+ * @brief database access for the merchant
+ * @author Florian Dold
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANTDB_PLUGIN_H
+#define TALER_MERCHANTDB_PLUGIN_H
+
+#include <gnunet/gnunet_util_lib.h>
+
+/**
+ * Handle to interact with the database.
+ */
+struct TALER_MERCHANTDB_Plugin;
+
+/**
+ * Handle to interact with the database.
+ */
+struct TALER_MERCHANTDB_Plugin
+{
+
+ /**
+ * Closure for all callbacks.
+ */
+ void *cls;
+
+ /**
+ * Name of the library which generated this plugin. Set by the
+ * plugin loader.
+ */
+ char *library_name;
+
+ /**
+ * Initialize merchant tables
+ *
+ * @param cls closure
+ * @param tmp #GNUNET_YES if the tables are to be made temporary i.e. their
+ * contents are dropped when the @a conn is closed
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+ int
+ (*initialize) (void *cls,
+ int tmp);
+
+ /**
+ * Insert payment confirmation from the mint into the database.
+ *
+ * @param cls closure
+ * @param h_contract hash of the contract
+ * @param h_wire hash of our wire details
+ * @param transaction_id of the contract
+ * @param timestamp time of the confirmation
+ * @param refund refund deadline
+ * @param amount_without_fee amount the mint will deposit
+ * @param coin_pub public key of the coin
+ * @param mint_proof proof from mint that coin was accepted
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error
+ */
+ int
+ (*store_payment) (void *cls,
+ const struct GNUNET_HashCode *h_contract,
+ const struct GNUNET_HashCode *h_wire,
+ uint64_t transaction_id,
+ struct GNUNET_TIME_Absolute timestamp,
+ struct GNUNET_TIME_Absolute refund,
+ const struct TALER_Amount *amount_without_fee,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ json_t *mint_proof);
+
+};
+
+#endif
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
new file mode 100644
index 00000000..8696db40
--- /dev/null
+++ b/src/lib/Makefile.am
@@ -0,0 +1,47 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+lib_LTLIBRARIES = \
+ libtalermerchant.la
+
+libtalermerchant_la_LDFLAGS = \
+ -version-info 0:0:0 \
+ -no-undefined
+
+libtalermerchant_la_SOURCES = \
+ merchant_api_context.c merchant_api_context.h \
+ merchant_api_json.c merchant_api_json.h \
+ merchant_api_pay.c
+
+libtalermerchant_la_LIBADD = \
+ -lgnunetutil \
+ -ljansson \
+ $(XLIB)
+
+if HAVE_LIBCURL
+libtalermerchant_la_LIBADD += -lcurl
+else
+if HAVE_LIBGNURL
+libtalermerchant_la_LIBADD += -lgnurl
+endif
+endif
+
+check_PROGRAMS = \
+ test_merchant_api
+
+TESTS = \
+ $(check_PROGRAMS)
+
+test_merchant_api_SOURCES = \
+ test_merchant_api.c
+test_merchant_api_LDADD = \
+ libtalermerchant.la \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil \
+ -ljansson
diff --git a/src/lib/merchant_api_context.c b/src/lib/merchant_api_context.c
new file mode 100644
index 00000000..5b8b9e4f
--- /dev/null
+++ b/src/lib/merchant_api_context.c
@@ -0,0 +1,525 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant-lib/merchant_api_context.c
+ * @brief Implementation of the context part of the merchant's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include "taler_merchant_service.h"
+#include "merchant_api_context.h"
+
+
+/**
+ * Log error related to CURL operations.
+ *
+ * @param type log level
+ * @param function which function failed to run
+ * @param code what was the curl error code
+ */
+#define CURL_STRERROR(type, function, code) \
+ GNUNET_log (type, \
+ "Curl function `%s' has failed at `%s:%d' with error: %s\n", \
+ function, __FILE__, __LINE__, curl_easy_strerror (code));
+
+/**
+ * Print JSON parsing related error information
+ */
+#define JSON_WARN(error) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
+ "JSON parsing failed at %s:%u: %s (%s)\n", \
+ __FILE__, __LINE__, error.text, error.source)
+
+
+/**
+ * Failsafe flag. Raised if our constructor fails to initialize
+ * the Curl library.
+ */
+static int TALER_MERCHANT_curl_fail;
+
+
+/**
+ * Jobs are CURL requests running within a `struct TALER_MERCHANT_Context`.
+ */
+struct MAC_Job
+{
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct MAC_Job *next;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct MAC_Job *prev;
+
+ /**
+ * Easy handle of the job.
+ */
+ CURL *easy_handle;
+
+ /**
+ * Context this job runs in.
+ */
+ struct TALER_MERCHANT_Context *ctx;
+
+ /**
+ * Function to call upon completion.
+ */
+ MAC_JobCompletionCallback jcc;
+
+ /**
+ * Closure for @e jcc.
+ */
+ void *jcc_cls;
+
+};
+
+
+/**
+ * Context
+ */
+struct TALER_MERCHANT_Context
+{
+ /**
+ * Curl multi handle
+ */
+ CURLM *multi;
+
+ /**
+ * Curl share handle
+ */
+ CURLSH *share;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct MAC_Job *jobs_head;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct MAC_Job *jobs_tail;
+
+ /**
+ * HTTP header "application/json", created once and used
+ * for all requests that need it.
+ */
+ struct curl_slist *json_header;
+
+};
+
+
+/**
+ * Initialise this library. This function should be called before using any of
+ * the following functions.
+ *
+ * @return library context
+ */
+struct TALER_MERCHANT_Context *
+TALER_MERCHANT_init ()
+{
+ struct TALER_MERCHANT_Context *ctx;
+ CURLM *multi;
+ CURLSH *share;
+
+ if (TALER_MERCHANT_curl_fail)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Curl was not initialised properly\n");
+ return NULL;
+ }
+ if (NULL == (multi = curl_multi_init ()))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to create a Curl multi handle\n");
+ return NULL;
+ }
+ if (NULL == (share = curl_share_init ()))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to create a Curl share handle\n");
+ return NULL;
+ }
+ ctx = GNUNET_new (struct TALER_MERCHANT_Context);
+ ctx->multi = multi;
+ ctx->share = share;
+ GNUNET_assert (NULL != (ctx->json_header =
+ curl_slist_append (NULL,
+ "Content-Type: application/json")));
+ return ctx;
+}
+
+
+/**
+ * Schedule a CURL request to be executed and call the given @a jcc
+ * upon its completion. Note that the context will make use of the
+ * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can
+ * instead use #MAC_easy_to_closure to extract the @a jcc_cls argument
+ * from a valid @a eh afterwards.
+ *
+ * This function modifies the CURL handle to add the
+ * "Content-Type: application/json" header if @a add_json is set.
+ *
+ * @param ctx context to execute the job in
+ * @param eh curl easy handle for the request, will
+ * be executed AND cleaned up
+ * @param add_json add "application/json" content type header
+ * @param jcc callback to invoke upon completion
+ * @param jcc_cls closure for @a jcc
+ */
+struct MAC_Job *
+MAC_job_add (struct TALER_MERCHANT_Context *ctx,
+ CURL *eh,
+ int add_json,
+ MAC_JobCompletionCallback jcc,
+ void *jcc_cls)
+{
+ struct MAC_Job *job;
+
+ if (GNUNET_YES == add_json)
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_HTTPHEADER,
+ ctx->json_header));
+
+ job = GNUNET_new (struct MAC_Job);
+ job->easy_handle = eh;
+ job->ctx = ctx;
+ job->jcc = jcc;
+ job->jcc_cls = jcc_cls;
+ GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
+ ctx->jobs_tail,
+ job);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_PRIVATE,
+ job));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_SHARE,
+ ctx->share));
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_add_handle (ctx->multi,
+ eh));
+ return job;
+}
+
+
+/**
+ * Obtain the `jcc_cls` argument from an `eh` that was
+ * given to #MAC_job_add().
+ *
+ * @param eh easy handle that was used
+ * @return the `jcc_cls` that was given to #MAC_job_add().
+ */
+void *
+MAC_easy_to_closure (CURL *eh)
+{
+ struct MAC_Job *job;
+
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_getinfo (eh,
+ CURLINFO_PRIVATE,
+ (char **) &job));
+ return job->jcc_cls;
+}
+
+
+/**
+ * Cancel a job. Must only be called before the job completion
+ * callback is called for the respective job.
+ *
+ * @param job job to cancel
+ */
+void
+MAC_job_cancel (struct MAC_Job *job)
+{
+ struct TALER_MERCHANT_Context *ctx = job->ctx;
+
+ GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
+ ctx->jobs_tail,
+ job);
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_remove_handle (ctx->multi,
+ job->easy_handle));
+ curl_easy_cleanup (job->easy_handle);
+ GNUNET_free (job);
+}
+
+
+/**
+ * Run the main event loop for the Taler interaction.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_MERCHANT_perform (struct TALER_MERCHANT_Context *ctx)
+{
+ CURLMsg *cmsg;
+ struct MAC_Job *job;
+ int n_running;
+ int n_completed;
+
+ (void) curl_multi_perform (ctx->multi,
+ &n_running);
+ while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
+ &n_completed)))
+ {
+ /* Only documented return value is CURLMSG_DONE */
+ GNUNET_break (CURLMSG_DONE == cmsg->msg);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_getinfo (cmsg->easy_handle,
+ CURLINFO_PRIVATE,
+ (char *) &job));
+ GNUNET_assert (job->ctx == ctx);
+ job->jcc (job->jcc_cls,
+ cmsg->easy_handle);
+ MAC_job_cancel (job);
+ }
+}
+
+
+/**
+ * Obtain the information for a select() call to wait until
+ * #TALER_MERCHANT_perform() is ready again. Note that calling
+ * any other TALER_MERCHANT-API may also imply that the library
+ * is again ready for #TALER_MERCHANT_perform().
+ *
+ * Basically, a client should use this API to prepare for select(),
+ * then block on select(), then call #TALER_MERCHANT_perform() and then
+ * start again until the work with the context is done.
+ *
+ * This function will NOT zero out the sets and assumes that @a max_fd
+ * and @a timeout are already set to minimal applicable values. It is
+ * safe to give this API FD-sets and @a max_fd and @a timeout that are
+ * already initialized to some other descriptors that need to go into
+ * the select() call.
+ *
+ * @param ctx context to get the event loop information for
+ * @param read_fd_set will be set for any pending read operations
+ * @param write_fd_set will be set for any pending write operations
+ * @param except_fd_set is here because curl_multi_fdset() has this argument
+ * @param max_fd set to the highest FD included in any set;
+ * if the existing sets have no FDs in it, the initial
+ * value should be "-1". (Note that `max_fd + 1` will need
+ * to be passed to select().)
+ * @param timeout set to the timeout in milliseconds (!); -1 means
+ * no timeout (NULL, blocking forever is OK), 0 means to
+ * proceed immediately with #TALER_MERCHANT_perform().
+ */
+void
+TALER_MERCHANT_get_select_info (struct TALER_MERCHANT_Context *ctx,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ fd_set *except_fd_set,
+ int *max_fd,
+ long *timeout)
+{
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_fdset (ctx->multi,
+ read_fd_set,
+ write_fd_set,
+ except_fd_set,
+ max_fd));
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_timeout (ctx->multi,
+ timeout));
+ if ( (-1 == (*timeout)) &&
+ (NULL != ctx->jobs_head) )
+ *timeout = 1000 * 60 * 5; /* curl is not always good about giving timeouts */
+}
+
+
+/**
+ * Cleanup library initialisation resources. This function should be called
+ * after using this library to cleanup the resources occupied during library's
+ * initialisation.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_MERCHANT_fini (struct TALER_MERCHANT_Context *ctx)
+{
+ /* all jobs must have been cancelled at this time, assert this */
+ GNUNET_assert (NULL == ctx->jobs_head);
+ curl_share_cleanup (ctx->share);
+ curl_multi_cleanup (ctx->multi);
+ curl_slist_free_all (ctx->json_header);
+ GNUNET_free (ctx);
+}
+
+
+/**
+ * Callback used when downloading the reply to an HTTP request.
+ * Just appends all of the data to the `buf` in the
+ * `struct MAC_DownloadBuffer` for further processing. The size of
+ * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
+ * the download exceeds this size, we abort with an error.
+ *
+ * @param bufptr data downloaded via HTTP
+ * @param size size of an item in @a bufptr
+ * @param nitems number of items in @a bufptr
+ * @param cls the `struct KeysRequest`
+ * @return number of bytes processed from @a bufptr
+ */
+size_t
+MAC_download_cb (char *bufptr,
+ size_t size,
+ size_t nitems,
+ void *cls)
+{
+ struct MAC_DownloadBuffer *db = cls;
+ size_t msize;
+ void *buf;
+
+ if (0 == size * nitems)
+ {
+ /* Nothing (left) to do */
+ return 0;
+ }
+ msize = size * nitems;
+ if ( (msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
+ {
+ db->eno = ENOMEM;
+ return 0; /* signals an error to curl */
+ }
+ db->buf = GNUNET_realloc (db->buf,
+ db->buf_size + msize);
+ buf = db->buf + db->buf_size;
+ memcpy (buf, bufptr, msize);
+ db->buf_size += msize;
+ return msize;
+}
+
+
+/**
+ * Obtain information about the final result about the
+ * HTTP download. If the download was successful, parses
+ * the JSON in the @a db and returns it. Also returns
+ * the HTTP @a response_code. If the download failed,
+ * the return value is NULL. The response code is set
+ * in any case, on download errors to zero.
+ *
+ * Calling this function also cleans up @a db.
+ *
+ * @param db download buffer
+ * @param eh CURL handle (to get the response code)
+ * @param[out] response_code set to the HTTP response code
+ * (or zero if we aborted the download, i.e.
+ * because the response was too big, or if
+ * the JSON we received was malformed).
+ * @return NULL if downloading a JSON reply failed
+ */
+json_t *
+MAC_download_get_result (struct MAC_DownloadBuffer *db,
+ CURL *eh,
+ long *response_code)
+{
+ json_t *json;
+ json_error_t error;
+ char *ct;
+
+ if ( (CURLE_OK !=
+ curl_easy_getinfo (eh,
+ CURLINFO_CONTENT_TYPE,
+ &ct)) ||
+ (NULL == ct) ||
+ (0 != strcasecmp (ct,
+ "application/json")) )
+ {
+ /* No content type or explicitly not JSON, refuse to parse
+ (but keep response code) */
+ if (CURLE_OK !=
+ curl_easy_getinfo (eh,
+ CURLINFO_RESPONSE_CODE,
+ response_code))
+ {
+ /* unexpected error... */
+ GNUNET_break (0);
+ *response_code = 0;
+ }
+ return NULL;
+ }
+
+ json = NULL;
+ if (0 == db->eno)
+ {
+ json = json_loadb (db->buf,
+ db->buf_size,
+ JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
+ &error);
+ if (NULL == json)
+ {
+ JSON_WARN (error);
+ *response_code = 0;
+ }
+ }
+ GNUNET_free_non_null (db->buf);
+ db->buf = NULL;
+ db->buf_size = 0;
+ if (NULL != json)
+ {
+ if (CURLE_OK !=
+ curl_easy_getinfo (eh,
+ CURLINFO_RESPONSE_CODE,
+ response_code))
+ {
+ /* unexpected error... */
+ GNUNET_break (0);
+ *response_code = 0;
+ }
+ }
+ return json;
+}
+
+
+/**
+ * Initial global setup logic, specifically runs the Curl setup.
+ */
+__attribute__ ((constructor))
+void
+TALER_MERCHANT_constructor__ (void)
+{
+ CURLcode ret;
+
+ if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
+ {
+ CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
+ "curl_global_init",
+ ret);
+ TALER_MERCHANT_curl_fail = 1;
+ }
+}
+
+
+/**
+ * Cleans up after us, specifically runs the Curl cleanup.
+ */
+__attribute__ ((destructor))
+void
+TALER_MERCHANT_destructor__ (void)
+{
+ if (TALER_MERCHANT_curl_fail)
+ return;
+ curl_global_cleanup ();
+}
+
+/* end of merchant_api_context.c */
diff --git a/src/lib/merchant_api_context.h b/src/lib/merchant_api_context.h
new file mode 100644
index 00000000..e54d34cc
--- /dev/null
+++ b/src/lib/merchant_api_context.h
@@ -0,0 +1,169 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant-lib/merchant_api_context.h
+ * @brief Internal interface to the context part of the merchant's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_merchant_service.h"
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Entry in the context's job queue.
+ */
+struct MAC_Job;
+
+/**
+ * Function to call upon completion of a job.
+ *
+ * @param cls closure
+ * @param eh original easy handle (for inspection)
+ */
+typedef void
+(*MAC_JobCompletionCallback)(void *cls,
+ CURL *eh);
+
+
+/**
+ * Schedule a CURL request to be executed and call the given @a jcc
+ * upon its completion. Note that the context will make use of the
+ * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can
+ * instead use #MAC_easy_to_closure to extract the @a jcc_cls argument
+ * from a valid @a eh afterwards.
+ *
+ * This function modifies the CURL handle to add the
+ * "Content-Type: application/json" header if @a add_json is set.
+ *
+ * @param ctx context to execute the job in
+ * @param eh curl easy handle for the request, will
+ * be executed AND cleaned up
+ * @param add_json add "application/json" content type header
+ * @param jcc callback to invoke upon completion
+ * @param jcc_cls closure for @a jcc
+ */
+struct MAC_Job *
+MAC_job_add (struct TALER_MERCHANT_Context *ctx,
+ CURL *eh,
+ int add_json,
+ MAC_JobCompletionCallback jcc,
+ void *jcc_cls);
+
+
+/**
+ * Obtain the `jcc_cls` argument from an `eh` that was
+ * given to #MAC_job_add().
+ *
+ * @param eh easy handle that was used
+ * @return the `jcc_cls` that was given to #MAC_job_add().
+ */
+void *
+MAC_easy_to_closure (CURL *eh);
+
+
+/**
+ * Cancel a job. Must only be called before the job completion
+ * callback is called for the respective job.
+ *
+ * @param job job to cancel
+ */
+void
+MAC_job_cancel (struct MAC_Job *job);
+
+
+/**
+ * @brief Buffer data structure we use to buffer the HTTP download
+ * before giving it to the JSON parser.
+ */
+struct MAC_DownloadBuffer
+{
+
+ /**
+ * Download buffer
+ */
+ void *buf;
+
+ /**
+ * The size of the download buffer
+ */
+ size_t buf_size;
+
+ /**
+ * Error code (based on libc errno) if we failed to download
+ * (i.e. response too large).
+ */
+ int eno;
+
+};
+
+
+/**
+ * Callback used when downloading the reply to an HTTP request.
+ * Just appends all of the data to the `buf` in the
+ * `struct MAC_DownloadBuffer` for further processing. The size of
+ * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
+ * the download exceeds this size, we abort with an error.
+ *
+ * Should be used by the various routines as the
+ * CURLOPT_WRITEFUNCTION. A `struct MAC_DownloadBuffer` needs to be
+ * passed to the CURLOPT_WRITEDATA.
+ *
+ * Afterwards, `eno` needs to be checked to ensure that the download
+ * completed correctly.
+ *
+ * @param bufptr data downloaded via HTTP
+ * @param size size of an item in @a bufptr
+ * @param nitems number of items in @a bufptr
+ * @param cls the `struct KeysRequest`
+ * @return number of bytes processed from @a bufptr
+ */
+size_t
+MAC_download_cb (char *bufptr,
+ size_t size,
+ size_t nitems,
+ void *cls);
+
+
+/**
+ * Obtain information about the final result about the
+ * HTTP download. If the download was successful, parses
+ * the JSON in the @a db and returns it. Also returns
+ * the HTTP @a response_code. If the download failed,
+ * the return value is NULL. The response code is set
+ * in any case, on download errors to zero.
+ *
+ * Calling this function also cleans up @a db.
+ *
+ * @param db download buffer
+ * @param eh CURL handle (to get the response code)
+ * @param[out] response_code set to the HTTP response code
+ * (or zero if we aborted the download, i.e.
+ * because the response was too big, or if
+ * the JSON we received was malformed).
+ * @return NULL if downloading a JSON reply failed
+ */
+json_t *
+MAC_download_get_result (struct MAC_DownloadBuffer *db,
+ CURL *eh,
+ long *response_code);
+
+
+/* end of merchant_api_context.h */
diff --git a/src/lib/merchant_api_json.c b/src/lib/merchant_api_json.c
new file mode 100644
index 00000000..9d03db65
--- /dev/null
+++ b/src/lib/merchant_api_json.c
@@ -0,0 +1,491 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/merchant_api_json.c
+ * @brief functions to parse incoming requests (JSON snippets)
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "merchant_api_json.h"
+
+/**
+ * Navigate and parse data in a JSON tree.
+ *
+ * @param root the JSON node to start the navigation at.
+ * @param spec parse specification array
+ * @return offset in @a spec where parsing failed, -1 on success (!)
+ */
+static int
+parse_json (json_t *root,
+ struct MAJ_Specification *spec)
+{
+ int i;
+ json_t *pos; /* what's our current position? */
+
+ pos = root;
+ for (i=0;MAJ_CMD_END != spec[i].cmd;i++)
+ {
+ pos = json_object_get (root,
+ spec[i].field);
+ if (NULL == pos)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ switch (spec[i].cmd)
+ {
+ case MAJ_CMD_END:
+ GNUNET_assert (0);
+ return i;
+ case MAJ_CMD_AMOUNT:
+ if (GNUNET_OK !=
+ TALER_json_to_amount (pos,
+ spec[i].details.amount))
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ break;
+ case MAJ_CMD_TIME_ABSOLUTE:
+ if (GNUNET_OK !=
+ TALER_json_to_abs (pos,
+ spec[i].details.abs_time))
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ break;
+
+ case MAJ_CMD_STRING:
+ {
+ const char *str;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ *spec[i].details.strptr = str;
+ }
+ break;
+
+ case MAJ_CMD_BINARY_FIXED:
+ {
+ const char *str;
+ int res;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ res = GNUNET_STRINGS_string_to_data (str, strlen (str),
+ spec[i].details.fixed_data.dest,
+ spec[i].details.fixed_data.dest_size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ }
+ break;
+
+ case MAJ_CMD_BINARY_VARIABLE:
+ {
+ const char *str;
+ size_t size;
+ void *data;
+ int res;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ size = (strlen (str) * 5) / 8;
+ if (size >= 1024)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ data = GNUNET_malloc (size);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ data,
+ size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (data);
+ return i;
+ }
+ *spec[i].details.variable_data.dest_p = data;
+ *spec[i].details.variable_data.dest_size_p = size;
+ }
+ break;
+
+ case MAJ_CMD_RSA_PUBLIC_KEY:
+ {
+ size_t size;
+ const char *str;
+ int res;
+ void *buf;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ size = (strlen (str) * 5) / 8;
+ buf = GNUNET_malloc (size);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ buf,
+ size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_free (buf);
+ GNUNET_break_op (0);
+ return i;
+ }
+ *spec[i].details.rsa_public_key
+ = GNUNET_CRYPTO_rsa_public_key_decode (buf,
+ size);
+ GNUNET_free (buf);
+ if (NULL == spec[i].details.rsa_public_key)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ }
+ break;
+
+ case MAJ_CMD_RSA_SIGNATURE:
+ {
+ size_t size;
+ const char *str;
+ int res;
+ void *buf;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ size = (strlen (str) * 5) / 8;
+ buf = GNUNET_malloc (size);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ buf,
+ size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_free (buf);
+ GNUNET_break_op (0);
+ return i;
+ }
+ *spec[i].details.rsa_signature
+ = GNUNET_CRYPTO_rsa_signature_decode (buf,
+ size);
+ GNUNET_free (buf);
+ if (NULL == spec[i].details.rsa_signature)
+ return i;
+ }
+ break;
+
+ case MAJ_CMD_UINT16:
+ {
+ json_int_t val;
+
+ if (! json_is_integer (pos))
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ val = json_integer_value (pos);
+ if ( (0 > val) || (val > UINT16_MAX) )
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ *spec[i].details.u16 = (uint16_t) val;
+ }
+ break;
+
+ case MAJ_CMD_JSON_OBJECT:
+ {
+ if (! (json_is_object (pos) || json_is_array (pos)) )
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ json_incref (pos);
+ *spec[i].details.obj = pos;
+ }
+ break;
+
+ default:
+ GNUNET_break (0);
+ return i;
+ }
+ }
+ return -1; /* all OK! */
+}
+
+
+/**
+ * Free all elements allocated during a
+ * #MAJ_parse_json() operation.
+ *
+ * @param spec specification of the parse operation
+ * @param end number of elements in @a spec to process
+ */
+static void
+parse_free (struct MAJ_Specification *spec,
+ int end)
+{
+ int i;
+
+ for (i=0;i<end;i++)
+ {
+ switch (spec[i].cmd)
+ {
+ case MAJ_CMD_END:
+ GNUNET_assert (0);
+ return;
+ case MAJ_CMD_AMOUNT:
+ break;
+ case MAJ_CMD_TIME_ABSOLUTE:
+ break;
+ case MAJ_CMD_BINARY_FIXED:
+ break;
+ case MAJ_CMD_STRING:
+ break;
+ case MAJ_CMD_BINARY_VARIABLE:
+ GNUNET_free (*spec[i].details.variable_data.dest_p);
+ *spec[i].details.variable_data.dest_p = NULL;
+ *spec[i].details.variable_data.dest_size_p = 0;
+ break;
+ case MAJ_CMD_RSA_PUBLIC_KEY:
+ GNUNET_CRYPTO_rsa_public_key_free (*spec[i].details.rsa_public_key);
+ *spec[i].details.rsa_public_key = NULL;
+ break;
+ case MAJ_CMD_RSA_SIGNATURE:
+ GNUNET_CRYPTO_rsa_signature_free (*spec[i].details.rsa_signature);
+ *spec[i].details.rsa_signature = NULL;
+ break;
+ case MAJ_CMD_JSON_OBJECT:
+ json_decref (*spec[i].details.obj);
+ *spec[i].details.obj = NULL;
+ break;
+ default:
+ GNUNET_break (0);
+ break;
+ }
+ }
+}
+
+
+/**
+ * Navigate and parse data in a JSON tree.
+ *
+ * @param root the JSON node to start the navigation at.
+ * @param spec parse specification array
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+MAJ_parse_json (const json_t *root,
+ struct MAJ_Specification *spec)
+{
+ int ret;
+
+ ret = parse_json ((json_t *) root,
+ spec);
+ if (-1 == ret)
+ return GNUNET_OK;
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "JSON field `%s` (%d) had unexpected value\n",
+ spec[ret].field,
+ ret);
+ parse_free (spec, ret);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Free all elements allocated during a
+ * #MAJ_parse_json() operation.
+ *
+ * @param spec specification of the parse operation
+ */
+void
+MAJ_parse_free (struct MAJ_Specification *spec)
+{
+ int i;
+
+ for (i=0;MAJ_CMD_END != spec[i].cmd;i++) ;
+ parse_free (spec, i);
+}
+
+
+/**
+ * The expected field stores a string.
+ *
+ * @param name name of the JSON field
+ * @param strptr where to store a pointer to the field
+ */
+struct MAJ_Specification
+MAJ_spec_string (const char *name,
+ const char **strptr)
+{
+ struct MAJ_Specification ret =
+ {
+ .cmd = MAJ_CMD_STRING,
+ .field = name,
+ .details.strptr = strptr
+ };
+ return ret;
+}
+
+
+/**
+ * Specification for parsing an absolute time value.
+ *
+ * @param name name of the JSON field
+ * @param at where to store the absolute time found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_absolute_time (const char *name,
+ struct GNUNET_TIME_Absolute *at)
+{
+ struct MAJ_Specification ret =
+ {
+ .cmd = MAJ_CMD_TIME_ABSOLUTE,
+ .field = name,
+ .details.abs_time = at
+ };
+ return ret;
+}
+
+
+/**
+ * Specification for parsing an amount value.
+ *
+ * @param name name of the JSON field
+ * @param amount where to store the amount found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_amount (const char *name,
+ struct TALER_Amount *amount)
+{
+ struct MAJ_Specification ret =
+ {
+ .cmd = MAJ_CMD_AMOUNT,
+ .field = name,
+ .details.amount = amount
+ };
+ return ret;
+}
+
+
+/**
+ * 16-bit integer.
+ *
+ * @param name name of the JSON field
+ * @param[out] u16 where to store the integer found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_uint16 (const char *name,
+ uint16_t *u16)
+{
+ struct MAJ_Specification ret =
+ {
+ .cmd = MAJ_CMD_UINT16,
+ .field = name,
+ .details.u16 = u16
+ };
+ return ret;
+}
+
+
+/**
+ * JSON object.
+ *
+ * @param name name of the JSON field
+ * @param[out] jsonp where to store the JSON found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_json (const char *name,
+ json_t **jsonp)
+{
+ struct MAJ_Specification ret =
+ {
+ .cmd = MAJ_CMD_JSON_OBJECT,
+ .field = name,
+ .details.obj = jsonp
+ };
+ return ret;
+}
+
+
+/**
+ * Specification for parsing an RSA public key.
+ *
+ * @param name name of the JSON field
+ * @param pk where to store the RSA key found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_rsa_public_key (const char *name,
+ struct GNUNET_CRYPTO_rsa_PublicKey **pk)
+{
+ struct MAJ_Specification ret =
+ {
+ .cmd = MAJ_CMD_RSA_PUBLIC_KEY,
+ .field = name,
+ .details.rsa_public_key = pk
+ };
+ return ret;
+}
+
+
+/**
+ * Specification for parsing an RSA signature.
+ *
+ * @param name name of the JSON field
+ * @param sig where to store the RSA signature found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_rsa_signature (const char *name,
+ struct GNUNET_CRYPTO_rsa_Signature **sig)
+{
+ struct MAJ_Specification ret =
+ {
+ .cmd = MAJ_CMD_RSA_SIGNATURE,
+ .field = name,
+ .details.rsa_signature = sig
+ };
+ return ret;
+}
+
+
+/* end of merchant_api_json.c */
diff --git a/src/lib/merchant_api_json.h b/src/lib/merchant_api_json.h
new file mode 100644
index 00000000..5f5265cf
--- /dev/null
+++ b/src/lib/merchant_api_json.h
@@ -0,0 +1,331 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/merchant_api_json.h
+ * @brief functions to parse incoming requests (JSON snippets)
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <taler/taler_util.h>
+#include <jansson.h>
+
+
+/**
+ * Enumeration with the various commands for the
+ * #MAJ_parse_json interpreter.
+ */
+enum MAJ_Command
+{
+
+ /**
+ * End of command list.
+ */
+ MAJ_CMD_END,
+
+ /**
+ * Parse amount at current position.
+ */
+ MAJ_CMD_AMOUNT,
+
+ /**
+ * Parse absolute time at current position.
+ */
+ MAJ_CMD_TIME_ABSOLUTE,
+
+ /**
+ * Parse fixed binary value at current position.
+ */
+ MAJ_CMD_BINARY_FIXED,
+
+ /**
+ * Parse variable-size binary value at current position.
+ */
+ MAJ_CMD_BINARY_VARIABLE,
+
+ /**
+ * Parse RSA public key at current position.
+ */
+ MAJ_CMD_RSA_PUBLIC_KEY,
+
+ /**
+ * Parse RSA signature at current position.
+ */
+ MAJ_CMD_RSA_SIGNATURE,
+
+ /**
+ * Parse `const char *` JSON string at current position.
+ */
+ MAJ_CMD_STRING,
+
+ /**
+ * Parse `uint16_t` integer at the current position.
+ */
+ MAJ_CMD_UINT16,
+
+ /**
+ * Parse JSON object at the current position.
+ */
+ MAJ_CMD_JSON_OBJECT,
+
+ /**
+ * Parse ??? at current position.
+ */
+ MAJ_CMD_C
+
+};
+
+
+/**
+ * @brief Entry in parser specification for #MAJ_parse_json.
+ */
+struct MAJ_Specification
+{
+
+ /**
+ * Command to execute.
+ */
+ enum MAJ_Command cmd;
+
+ /**
+ * Name of the field to access.
+ */
+ const char *field;
+
+ /**
+ * Further details for the command.
+ */
+ union {
+
+ /**
+ * Where to store amount for #MAJ_CMD_AMOUNT.
+ */
+ struct TALER_Amount *amount;
+
+ /**
+ * Where to store time, for #MAJ_CMD_TIME_ABSOLUTE.
+ */
+ struct GNUNET_TIME_Absolute *abs_time;
+
+ /**
+ * Where to write binary data, for #MAJ_CMD_BINARY_FIXED.
+ */
+ struct {
+ /**
+ * Where to write the data.
+ */
+ void *dest;
+
+ /**
+ * How many bytes to write to @e dest.
+ */
+ size_t dest_size;
+
+ } fixed_data;
+
+ /**
+ * Where to write binary data, for #MAJ_CMD_BINARY_VARIABLE.
+ */
+ struct {
+ /**
+ * Where to store the pointer with the data (is allocated).
+ */
+ void **dest_p;
+
+ /**
+ * Where to store the number of bytes allocated at `*dest`.
+ */
+ size_t *dest_size_p;
+
+ } variable_data;
+
+ /**
+ * Where to store the RSA public key for #MAJ_CMD_RSA_PUBLIC_KEY
+ */
+ struct GNUNET_CRYPTO_rsa_PublicKey **rsa_public_key;
+
+ /**
+ * Where to store the RSA signature for #MAJ_CMD_RSA_SIGNATURE
+ */
+ struct GNUNET_CRYPTO_rsa_Signature **rsa_signature;
+
+ /**
+ * Details for #MAJ_CMD_EDDSA_SIGNATURE
+ */
+ struct {
+
+ /**
+ * Where to store the purpose.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose **purpose_p;
+
+ /**
+ * Key to verify the signature against.
+ */
+ const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key;
+
+ } eddsa_signature;
+
+ /**
+ * Where to store a pointer to the string.
+ */
+ const char **strptr;
+
+ /**
+ * Where to store 16-bit integer.
+ */
+ uint16_t *u16;
+
+ /**
+ * Where to store a JSON object.
+ */
+ json_t **obj;
+
+ } details;
+
+};
+
+
+/**
+ * Navigate and parse data in a JSON tree.
+ *
+ * @param root the JSON node to start the navigation at.
+ * @param spec parse specification array
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+MAJ_parse_json (const json_t *root,
+ struct MAJ_Specification *spec);
+
+
+/**
+ * Free all elements allocated during a
+ * #MAJ_parse_json() operation.
+ *
+ * @param spec specification of the parse operation
+ */
+void
+MAJ_parse_free (struct MAJ_Specification *spec);
+
+
+/**
+ * End of a parser specification.
+ */
+#define MAJ_spec_end { .cmd = MAJ_CMD_END }
+
+/**
+ * Fixed size object (in network byte order, encoded using Crockford
+ * Base32hex encoding).
+ *
+ * @param name name of the JSON field
+ * @param obj pointer where to write the data (type of `*obj` will determine size)
+ */
+#define MAJ_spec_fixed_auto(name,obj) { .cmd = MAJ_CMD_BINARY_FIXED, .field = name, .details.fixed_data.dest = obj, .details.fixed_data.dest_size = sizeof (*obj) }
+
+
+/**
+ * Variable size object (in network byte order, encoded using Crockford
+ * Base32hex encoding).
+ *
+ * @param name name of the JSON field
+ * @param obj pointer where to write the data (a `void **`)
+ * @param size where to store the number of bytes allocated for @a obj (of type `size_t *`
+ */
+#define MAJ_spec_varsize(name,obj,size) { .cmd = MAJ_CMD_BINARY_VARIABLE, .field = name, .details.variable_data.dest_p = obj, .details.variable_data.dest_size_p = size }
+
+
+/**
+ * The expected field stores a string.
+ *
+ * @param name name of the JSON field
+ * @param strptr where to store a pointer to the field
+ */
+struct MAJ_Specification
+MAJ_spec_string (const char *name,
+ const char **strptr);
+
+
+/**
+ * Absolute time.
+ *
+ * @param name name of the JSON field
+ * @param[out] at where to store the absolute time found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_absolute_time (const char *name,
+ struct GNUNET_TIME_Absolute *at);
+
+
+/**
+ * 16-bit integer.
+ *
+ * @param name name of the JSON field
+ * @param[out] u16 where to store the integer found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_uint16 (const char *name,
+ uint16_t *u16);
+
+
+/**
+ * JSON object.
+ *
+ * @param name name of the JSON field
+ * @param[out] jsonp where to store the JSON found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_json (const char *name,
+ json_t **jsonp);
+
+
+/**
+ * Specification for parsing an amount value.
+ *
+ * @param name name of the JSON field
+ * @param amount where to store the amount under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_amount (const char *name,
+ struct TALER_Amount *amount);
+
+
+/**
+ * Specification for parsing an RSA public key.
+ *
+ * @param name name of the JSON field
+ * @param pk where to store the RSA key found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_rsa_public_key (const char *name,
+ struct GNUNET_CRYPTO_rsa_PublicKey **pk);
+
+
+/**
+ * Specification for parsing an RSA signature.
+ *
+ * @param name name of the JSON field
+ * @param sig where to store the RSA signature found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_rsa_signature (const char *name,
+ struct GNUNET_CRYPTO_rsa_Signature **sig);
+
+
+
+
+/* end of merchant_api_json.h */
diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c
new file mode 100644
index 00000000..e33da423
--- /dev/null
+++ b/src/lib/merchant_api_pay.c
@@ -0,0 +1,372 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/merchant_api_pay.c
+ * @brief Implementation of the /pay request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_merchant_service.h"
+#include "merchant_api_json.h"
+#include "merchant_api_context.h"
+#include <taler/taler_signatures.h>
+
+
+/**
+ * @brief A Pay Handle
+ */
+struct TALER_MERCHANT_Pay
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * JSON encoding of the request to POST.
+ */
+ char *json_enc;
+
+ /**
+ * Handle for the request.
+ */
+ struct MAC_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PayCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Download buffer
+ */
+ struct MAC_DownloadBuffer db;
+
+};
+
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /pay request.
+ *
+ * @param cls the `struct TALER_MERCHANT_Pay`
+ * @param eh the curl request handle
+ */
+static void
+handle_pay_finished (void *cls,
+ CURL *eh)
+{
+ struct TALER_MERCHANT_Pay *ph = cls;
+ long response_code;
+ json_t *json;
+
+ ph->job = NULL;
+ json = MAC_download_get_result (&ph->db,
+ eh,
+ &response_code);
+ switch (response_code)
+ {
+ case 0:
+ break;
+ case MHD_HTTP_OK:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* This should never happen, either us or the merchant is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ /* Nothing really to verify, merchant says one of the signatures is
+ invalid; as we checked them, this should never happen, we
+ should pass the JSON reply to the application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ response_code);
+ GNUNET_break (0);
+ response_code = 0;
+ break;
+ }
+ ph->cb (ph->cb_cls,
+ response_code,
+ json);
+ json_decref (json);
+ TALER_MERCHANT_pay_cancel (ph);
+}
+
+
+/**
+ * Pay a merchant. API for wallets that have the coin's private keys.
+ *
+ * @param merchant the merchant context
+ * @param mint_uri URI of the mint that the coins belong to
+ * @param h_wire hash of the merchant’s account details
+ * @param h_contract hash of the contact of the merchant with the customer
+ * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
+ * @param transaction_id transaction id for the transaction between merchant and customer
+ * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
+ * @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed)
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key.
+ * @param pay_cb the callback to call when a reply for this request is available
+ * @param pay_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_Pay *
+TALER_MERCHANT_pay_wallet (struct TALER_MERCHANT_Context *merchant,
+ const char *mint_uri,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ struct GNUNET_TIME_Absolute timestamp,
+ uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ struct GNUNET_TIME_Absolute refund_deadline,
+ unsigned int num_coins,
+ const struct TLAER_MERCHANT_PayCoin *coins,
+ TALER_MERCHANT_PayCallback pay_cb,
+ void *pay_cb_cls)
+{
+ struct TALER_MERCHANT_Pay *ph;
+ json_t *pay_obj;
+ json_t *j_coins;
+ CURL *eh;
+ struct GNUNET_HashCode h_wire;
+ struct TALER_Amount amount_without_fee;
+ unsigned int i;
+ struct TALER_DepositRequestPS dr;
+
+ dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
+ dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
+ dr.h_contract = *h_contract;
+ dr.h_wire = *h_wire;
+ dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+ dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
+ dr.transaction_id = GNUNET_htonll (transaction_id);
+ dr.merchant = *merchant_pub;
+ j_coins = json_array ();
+ for (i=0;i<num_coins;i++)
+ {
+ json_t *j_coin;
+ const struct TALER_MERCHANT_PayCoin *pc = &coins[i];
+ struct TALER_CoinSpendSignatureP coin_sig;
+ struct TALER_Amount fee;
+
+ /* prepare 'dr' for this coin to generate coin signature */
+ GNUNET_CRYPTO_ecdhe_key_get_public (&pc->coin_priv.edche_priv,
+ &dr.coin_pub.ecdhe_pub);
+ TALER_amount_hton (&dr.amount_with_fee,
+ &pc->amount_with_fee);
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&fee,
+ &pc->amount_with_fee,
+ &pc->fee))
+ {
+ /* Integer underflow, fee larger than total amount?
+ This should not happen (client violated API!) */
+ GNUNET_break (0);
+ json_decref (j_coins);
+ return NULL;
+ }
+ TALER_amount_hton (&dr.deposit_fee,
+ &fee);
+ GNUNET_CRYPTO_eddsa_sign (&pc->coin_priv.eddsa_priv,
+ &dr.purpose,
+ &coin_sig.eddsa_sig);
+
+ /* create JSON for this coin */
+ j_coin = json_pack ("{s:o, s:o," /* f/coin_pub */
+ " s:o, s:o," /* denom_pub / ub_sig */
+ " s:o}", /* coin_sig */
+ "f", TALER_json_from_amount (&pc->amount_with_fee),
+ "coin_pub", TALER_json_from_data (&dr.coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP)),
+ "denom_pub", TALER_json_from_rsa_public_key (pc->denom_pub.rsa_public_key),
+ "ub_sig", TALER_json_from_rsa_signature (pc->denom_sig.rsa_signature),
+ "coin_sig", TALER_json_from_data (&coin_sig,
+ sizeof (coin_sig))
+ );
+ json_array_append (j_coins,
+ j_coin);
+ }
+
+
+ pay_obj = json_pack ("{s:o, s:o," /* H_wire/H_contract */
+ " s:I, s:o," /* transaction id, timestamp */
+ " s:o, s:s," /* refund_deadline, mint */
+ " s:o, s:o," /* coins, max_fee */
+ " s:o}", /* amount */
+ "H_wire", TALER_json_from_data (&h_wire,
+ sizeof (h_wire)),
+ "H_contract", TALER_json_from_data (h_contract,
+ sizeof (struct GNUNET_HashCode)),
+ "transaction_id", (json_int_t) transaction_id,
+ "timestamp", TALER_json_from_abs (timestamp),
+ "refund_deadline", TALER_json_from_abs (refund_deadline),
+ "mint", mint_uri,
+ "coins", j_coins,
+ "max_fee", TALER_json_from_amount (max_fee),
+ "amount", TALER_json_from_amount (amount)
+ );
+
+ // optionally: add edate! "edate", TALER_json_from_abs (wire_deadline),
+
+ ph = GNUNET_new (struct TALER_MERCHANT_Pay);
+#if 0
+ ph->merchant = merchant;
+ ph->cb = cb;
+ ph->cb_cls = cb_cls;
+ ph->url = MAH_path_to_url (merchant, "/pay");
+ ph->depconf.purpose.size = htonl (sizeof (struct TALER_PayConfirmationPS));
+ ph->depconf.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONFIRM_PAY);
+ ph->depconf.h_contract = *h_contract;
+ ph->depconf.h_wire = h_wire;
+ ph->depconf.transaction_id = GNUNET_htonll (transaction_id);
+ ph->depconf.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+ ph->depconf.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
+ TALER_amount_subtract (&amount_without_fee,
+ amount,
+ &dki->fee_pay);
+ TALER_amount_hton (&ph->depconf.amount_without_fee,
+ &amount_without_fee);
+ ph->depconf.coin_pub = *coin_pub;
+ ph->depconf.merchant = *merchant_pub;
+ ph->amount_with_fee = *amount;
+ ph->coin_value = dki->value;
+
+ eh = curl_easy_init ();
+ GNUNET_assert (NULL != (ph->json_enc =
+ json_dumps (pay_obj,
+ JSON_COMPACT)));
+ json_decref (pay_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ ph->url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDS,
+ ph->json_enc));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDSIZE,
+ strlen (ph->json_enc)));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_WRITEFUNCTION,
+ &MAC_download_cb));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_WRITEDATA,
+ &ph->db));
+ ctx = MAH_handle_to_context (merchant);
+ ph->job = MAC_job_add (ctx,
+ eh,
+ GNUNET_YES,
+ &handle_pay_finished,
+ ph);
+ return ph;
+#endif
+ return NULL;
+}
+
+
+
+/**
+ * Pay a merchant. API for frontends talking to backends. Here,
+ * the frontend does not have the coin's private keys, but just
+ * the public keys and signatures. Note the sublte difference
+ * in the type of @a coins compared to #TALER_MERCHANT_pay().
+ *
+ * @param merchant the merchant context
+ * @param mint_uri URI of the mint that the coins belong to
+ * @param h_wire hash of the merchant’s account details
+ * @param h_contract hash of the contact of the merchant with the customer
+ * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
+ * @param transaction_id transaction id for the transaction between merchant and customer
+ * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
+ * @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed)
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key.
+ * @param pay_cb the callback to call when a reply for this request is available
+ * @param pay_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_Pay *
+TALER_MERCHANT_pay_frontend (struct TALER_MERCHANT_Context *merchant,
+ const char *mint_uri,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ struct GNUNET_TIME_Absolute timestamp,
+ uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ struct GNUNET_TIME_Absolute refund_deadline,
+ unsigned int num_coins,
+ const struct TALER_MERCHANT_PaidCoin *coins,
+ TALER_MERCHANT_PayCallback pay_cb,
+ void *pay_cb_cls)
+{
+ GNUNET_break (0); // FIXME: not implemented!
+ return NULL;
+}
+
+
+/**
+ * Cancel a pay permission request. This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param pay the pay permission request handle
+ */
+void
+TALER_MERCHANT_pay_cancel (struct TALER_MERCHANT_Pay *pay)
+{
+ if (NULL != pay->job)
+ {
+ MAC_job_cancel (pay->job);
+ pay->job = NULL;
+ }
+ GNUNET_free_non_null (pay->db.buf);
+ GNUNET_free (pay->url);
+ GNUNET_free (pay->json_enc);
+ GNUNET_free (pay);
+}
+
+
+/* end of merchant_api_pay.c */
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
new file mode 100644
index 00000000..c264f9a3
--- /dev/null
+++ b/src/lib/test_merchant_api.c
@@ -0,0 +1,1345 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant/test_merchant_api.c
+ * @brief testcase to test merchant's HTTP API interface
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "taler_mint_service.h"
+#include "taler_merchant_service.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+
+/**
+ * Main execution context for the main loop of the mint.
+ */
+static struct TALER_MINT_Context *ctx;
+
+/**
+ * Handle to access the mint.
+ */
+static struct TALER_MINT_Handle *mint;
+
+/**
+ * Main execution context for the main loop of the mint.
+ */
+static struct TALER_MERCHANT_Context *merchant;
+
+/**
+ * Task run on shutdown.
+ */
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
+
+/**
+ * Task that runs the main event loop.
+ */
+static struct GNUNET_SCHEDULER_Task *ctx_task;
+
+/**
+ * Result of the testcases, #GNUNET_OK on success
+ */
+static int result;
+
+
+/**
+ * Opcodes for the interpreter.
+ */
+enum OpCode
+{
+ /**
+ * Termination code, stops the interpreter loop (with success).
+ */
+ OC_END = 0,
+
+ /**
+ * Add funds to a reserve by (faking) incoming wire transfer.
+ */
+ OC_ADMIN_ADD_INCOMING,
+
+ /**
+ * Check status of a reserve.
+ */
+ OC_WITHDRAW_STATUS,
+
+ /**
+ * Withdraw a coin from a reserve.
+ */
+ OC_WITHDRAW_SIGN,
+
+ /**
+ * Pay with coins.
+ */
+ OC_PAY
+
+};
+
+
+/**
+ * Structure specifying details about a coin to be melted.
+ * Used in a NULL-terminated array as part of command
+ * specification.
+ */
+struct MeltDetails
+{
+
+ /**
+ * Amount to melt (including fee).
+ */
+ const char *amount;
+
+ /**
+ * Reference to reserve_withdraw operations for coin to
+ * be used for the /refresh/melt operation.
+ */
+ const char *coin_ref;
+
+};
+
+
+/**
+ * Information about a fresh coin generated by the refresh operation.
+ */
+struct FreshCoin
+{
+
+ /**
+ * If @e amount is NULL, this specifies the denomination key to
+ * use. Otherwise, this will be set (by the interpreter) to the
+ * denomination PK matching @e amount.
+ */
+ const struct TALER_MINT_DenomPublicKey *pk;
+
+ /**
+ * Set (by the interpreter) to the mint's signature over the
+ * coin's public key.
+ */
+ struct TALER_DenominationSignature sig;
+
+ /**
+ * Set (by the interpreter) to the coin's private key.
+ */
+ struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+};
+
+
+/**
+ * Details for a mint operation to execute.
+ */
+struct Command
+{
+ /**
+ * Opcode of the command.
+ */
+ enum OpCode oc;
+
+ /**
+ * Label for the command, can be NULL.
+ */
+ const char *label;
+
+ /**
+ * Which response code do we expect for this command?
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Details about the command.
+ */
+ union
+ {
+
+ /**
+ * Information for a #OC_ADMIN_ADD_INCOMING command.
+ */
+ struct
+ {
+
+ /**
+ * Label to another admin_add_incoming command if we
+ * should deposit into an existing reserve, NULL if
+ * a fresh reserve should be created.
+ */
+ const char *reserve_reference;
+
+ /**
+ * String describing the amount to add to the reserve.
+ */
+ const char *amount;
+
+ /**
+ * Wire details (JSON).
+ */
+ const char *wire;
+
+ /**
+ * Set (by the interpreter) to the reserve's private key
+ * we used to fill the reserve.
+ */
+ struct TALER_ReservePrivateKeyP reserve_priv;
+
+ /**
+ * Set to the API's handle during the operation.
+ */
+ struct TALER_MINT_AdminAddIncomingHandle *aih;
+
+ } admin_add_incoming;
+
+ /**
+ * Information for a #OC_WITHDRAW_STATUS command.
+ */
+ struct
+ {
+
+ /**
+ * Label to the #OC_ADMIN_ADD_INCOMING command which
+ * created the reserve.
+ */
+ const char *reserve_reference;
+
+ /**
+ * Set to the API's handle during the operation.
+ */
+ struct TALER_MINT_ReserveStatusHandle *wsh;
+
+ /**
+ * Expected reserve balance.
+ */
+ const char *expected_balance;
+
+ } reserve_status;
+
+ /**
+ * Information for a #OC_WITHDRAW_SIGN command.
+ */
+ struct
+ {
+
+ /**
+ * Which reserve should we withdraw from?
+ */
+ const char *reserve_reference;
+
+ /**
+ * String describing the denomination value we should withdraw.
+ * A corresponding denomination key must exist in the mint's
+ * offerings. Can be NULL if @e pk is set instead.
+ */
+ const char *amount;
+
+ /**
+ * If @e amount is NULL, this specifies the denomination key to
+ * use. Otherwise, this will be set (by the interpreter) to the
+ * denomination PK matching @e amount.
+ */
+ const struct TALER_MINT_DenomPublicKey *pk;
+
+ /**
+ * Set (by the interpreter) to the mint's signature over the
+ * coin's public key.
+ */
+ struct TALER_DenominationSignature sig;
+
+ /**
+ * Set (by the interpreter) to the coin's private key.
+ */
+ struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+ /**
+ * Blinding key used for the operation.
+ */
+ struct TALER_DenominationBlindingKey blinding_key;
+
+ /**
+ * Withdraw handle (while operation is running).
+ */
+ struct TALER_MINT_ReserveWithdrawHandle *wsh;
+
+ } reserve_withdraw;
+
+ /**
+ * Information for a #OC_DEPOSIT command.
+ */
+ struct
+ {
+
+ /**
+ * Amount to pay.
+ */
+ const char *amount;
+
+ /**
+ * Reference to a reserve_withdraw operation for a coin to
+ * be used for the /deposit operation.
+ */
+ const char *coin_ref;
+
+ /**
+ * If this @e coin_ref refers to an operation that generated
+ * an array of coins, this value determines which coin to use.
+ */
+ unsigned int coin_idx;
+
+ /**
+ * JSON string describing the merchant's "wire details".
+ */
+ const char *wire_details;
+
+ /**
+ * JSON string describing the contract between the two parties.
+ */
+ const char *contract;
+
+ /**
+ * Transaction ID to use.
+ */
+ uint64_t transaction_id;
+
+ /**
+ * Relative time (to add to 'now') to compute the refund deadline.
+ * Zero for no refunds.
+ */
+ struct GNUNET_TIME_Relative refund_deadline;
+
+ /**
+ * Set (by the interpreter) to a fresh private key of the merchant,
+ * if @e refund_deadline is non-zero.
+ */
+ struct TALER_MerchantPrivateKeyP merchant_priv;
+
+ /**
+ * Deposit handle while operation is running.
+ */
+ struct TALER_MINT_DepositHandle *dh;
+
+ } pay;
+
+ } details;
+
+};
+
+
+/**
+ * State of the interpreter loop.
+ */
+struct InterpreterState
+{
+ /**
+ * Keys from the mint.
+ */
+ const struct TALER_MINT_Keys *keys;
+
+ /**
+ * Commands the interpreter will run.
+ */
+ struct Command *commands;
+
+ /**
+ * Interpreter task (if one is scheduled).
+ */
+ struct GNUNET_SCHEDULER_Task *task;
+
+ /**
+ * Instruction pointer. Tells #interpreter_run() which
+ * instruction to run next.
+ */
+ unsigned int ip;
+
+};
+
+
+/**
+ * Task that runs the context's event loop with the GNUnet scheduler.
+ *
+ * @param cls unused
+ * @param tc scheduler context (unused)
+ */
+static void
+context_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Run the context task, the working set has changed.
+ */
+static void
+trigger_context_task ()
+{
+ GNUNET_SCHEDULER_cancel (ctx_task);
+ ctx_task = GNUNET_SCHEDULER_add_now (&context_task,
+ NULL);
+}
+
+
+/**
+ * The testcase failed, return with an error code.
+ *
+ * @param is interpreter state to clean up
+ */
+static void
+fail (struct InterpreterState *is)
+{
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Find a command by label.
+ *
+ * @param is interpreter state to search
+ * @param label label to look for
+ * @return NULL if command was not found
+ */
+static const struct Command *
+find_command (const struct InterpreterState *is,
+ const char *label)
+{
+ unsigned int i;
+ const struct Command *cmd;
+
+ if (NULL == label)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Attempt to lookup command for empty label\n");
+ return NULL;
+ }
+ for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
+ if ( (NULL != cmd->label) &&
+ (0 == strcmp (cmd->label,
+ label)) )
+ return cmd;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command not found: %s\n",
+ label);
+ return NULL;
+}
+
+
+/**
+ * Run the main interpreter loop that performs mint operations.
+ *
+ * @param cls contains the `struct InterpreterState`
+ * @param tc scheduler context
+ */
+static void
+interpreter_run (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Function called upon completion of our /admin/add/incoming request.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
+ * 0 if the mint's reply is bogus (fails to follow the protocol)
+ * @param full_response full response from the mint (for logging, in case of errors)
+ */
+static void
+add_incoming_cb (void *cls,
+ unsigned int http_status,
+ json_t *full_response)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+
+ cmd->details.admin_add_incoming.aih = NULL;
+ if (MHD_HTTP_OK != http_status)
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ is->ip++;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
+ * Check if the given historic event @a h corresponds to the given
+ * command @a cmd.
+ *
+ * @param h event in history
+ * @param cmd an #OC_ADMIN_ADD_INCOMING command
+ * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not
+ */
+static int
+compare_admin_add_incoming_history (const struct TALER_MINT_ReserveHistory *h,
+ const struct Command *cmd)
+{
+ struct TALER_Amount amount;
+
+ if (TALER_MINT_RTT_DEPOSIT != h->type)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
+ &amount));
+ if (0 != TALER_amount_cmp (&amount,
+ &h->amount))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Check if the given historic event @a h corresponds to the given
+ * command @a cmd.
+ *
+ * @param h event in history
+ * @param cmd an #OC_WITHDRAW_SIGN command
+ * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not
+ */
+static int
+compare_reserve_withdraw_history (const struct TALER_MINT_ReserveHistory *h,
+ const struct Command *cmd)
+{
+ struct TALER_Amount amount;
+ struct TALER_Amount amount_with_fee;
+
+ if (TALER_MINT_RTT_WITHDRAWAL != h->type)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (cmd->details.reserve_withdraw.amount,
+ &amount));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_add (&amount_with_fee,
+ &amount,
+ &cmd->details.reserve_withdraw.pk->fee_withdraw));
+ if (0 != TALER_amount_cmp (&amount_with_fee,
+ &h->amount))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called with the result of a /reserve/status request.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
+ * 0 if the mint's reply is bogus (fails to follow the protocol)
+ * @param[in] json original response in JSON format (useful only for diagnostics)
+ * @param balance current balance in the reserve, NULL on error
+ * @param history_length number of entries in the transaction history, 0 on error
+ * @param history detailed transaction history, NULL on error
+ */
+static void
+reserve_status_cb (void *cls,
+ unsigned int http_status,
+ json_t *json,
+ const struct TALER_Amount *balance,
+ unsigned int history_length,
+ const struct TALER_MINT_ReserveHistory *history)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+ struct Command *rel;
+ unsigned int i;
+ unsigned int j;
+ struct TALER_Amount amount;
+
+ cmd->details.reserve_status.wsh = NULL;
+ if (cmd->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ cmd->label);
+ GNUNET_break (0);
+ json_dumpf (json, stderr, 0);
+ fail (is);
+ return;
+ }
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ /* FIXME: note that history events may come in a different
+ order than the commands. However, for now this works... */
+ j = 0;
+ for (i=0;i<is->ip;i++)
+ {
+ switch ((rel = &is->commands[i])->oc)
+ {
+ case OC_ADMIN_ADD_INCOMING:
+ if ( ( (NULL != rel->label) &&
+ (0 == strcmp (cmd->details.reserve_status.reserve_reference,
+ rel->label) ) ) ||
+ ( (NULL != rel->details.admin_add_incoming.reserve_reference) &&
+ (0 == strcmp (cmd->details.reserve_status.reserve_reference,
+ rel->details.admin_add_incoming.reserve_reference) ) ) )
+ {
+ if (GNUNET_OK !=
+ compare_admin_add_incoming_history (&history[j],
+ rel))
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ j++;
+ }
+ break;
+ case OC_WITHDRAW_SIGN:
+ if (0 == strcmp (cmd->details.reserve_status.reserve_reference,
+ rel->details.reserve_withdraw.reserve_reference))
+ {
+ if (GNUNET_OK !=
+ compare_reserve_withdraw_history (&history[j],
+ rel))
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ j++;
+ }
+ break;
+ default:
+ /* unreleated, just skip */
+ break;
+ }
+ }
+ if (j != history_length)
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ if (NULL != cmd->details.reserve_status.expected_balance)
+ {
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (cmd->details.reserve_status.expected_balance,
+ &amount));
+ if (0 != TALER_amount_cmp (&amount,
+ balance))
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ }
+ break;
+ default:
+ /* Unsupported status code (by test harness) */
+ GNUNET_break (0);
+ break;
+ }
+ is->ip++;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
+ * Function called upon completion of our /reserve/withdraw request.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
+ * 0 if the mint's reply is bogus (fails to follow the protocol)
+ * @param sig signature over the coin, NULL on error
+ * @param full_response full response from the mint (for logging, in case of errors)
+ */
+static void
+reserve_withdraw_cb (void *cls,
+ unsigned int http_status,
+ const struct TALER_DenominationSignature *sig,
+ json_t *full_response)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+
+ cmd->details.reserve_withdraw.wsh = NULL;
+ if (cmd->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ cmd->label);
+ json_dumpf (full_response, stderr, 0);
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ if (NULL == sig)
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ cmd->details.reserve_withdraw.sig.rsa_signature
+ = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature);
+ break;
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ /* nothing to check */
+ break;
+ default:
+ /* Unsupported status code (by test harness) */
+ GNUNET_break (0);
+ break;
+ }
+ is->ip++;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
+ * Function called with the result of a /pay operation.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit;
+ * 0 if the mint's reply is bogus (fails to follow the protocol)
+ * @param obj the received JSON reply, should be kept as proof (and, in case of errors,
+ * be forwarded to the customer)
+ */
+static void
+pay_cb (void *cls,
+ unsigned int http_status,
+ const char *redirect_uri,
+ json_t *obj)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+
+ cmd->details.deposit.dh = NULL;
+ if (cmd->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ cmd->label);
+ json_dumpf (obj, stderr, 0);
+ fail (is);
+ return;
+ }
+ is->ip++;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
+ * Find denomination key matching the given amount.
+ *
+ * @param keys array of keys to search
+ * @param amount coin value to look for
+ * @return NULL if no matching key was found
+ */
+static const struct TALER_MINT_DenomPublicKey *
+find_pk (const struct TALER_MINT_Keys *keys,
+ const struct TALER_Amount *amount)
+{
+ unsigned int i;
+ struct GNUNET_TIME_Absolute now;
+ struct TALER_MINT_DenomPublicKey *pk;
+ char *str;
+
+ now = GNUNET_TIME_absolute_get ();
+ for (i=0;i<keys->num_denom_keys;i++)
+ {
+ pk = &keys->denom_keys[i];
+ if ( (0 == TALER_amount_cmp (amount,
+ &pk->value)) &&
+ (now.abs_value_us >= pk->valid_from.abs_value_us) &&
+ (now.abs_value_us < pk->withdraw_valid_until.abs_value_us) )
+ return pk;
+ }
+ /* do 2nd pass to check if expiration times are to blame for failure */
+ str = TALER_amount_to_string (amount);
+ for (i=0;i<keys->num_denom_keys;i++)
+ {
+ pk = &keys->denom_keys[i];
+ if ( (0 == TALER_amount_cmp (amount,
+ &pk->value)) &&
+ ( (now.abs_value_us < pk->valid_from.abs_value_us) ||
+ (now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Have denomination key for `%s', but with wrong expiration range %llu vs [%llu,%llu)\n",
+ str,
+ now.abs_value_us,
+ pk->valid_from.abs_value_us,
+ pk->withdraw_valid_until.abs_value_us);
+ GNUNET_free (str);
+ return NULL;
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No denomination key for amount %s found\n",
+ str);
+ GNUNET_free (str);
+ return NULL;
+}
+
+
+/**
+ * Run the main interpreter loop that performs mint operations.
+ *
+ * @param cls contains the `struct InterpreterState`
+ * @param tc scheduler context
+ */
+static void
+interpreter_run (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+ const struct Command *ref;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_Amount amount;
+ struct GNUNET_TIME_Absolute execution_date;
+ json_t *wire;
+
+ is->task = NULL;
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ {
+ fprintf (stderr,
+ "Test aborted by shutdown request\n");
+ fail (is);
+ return;
+ }
+ switch (cmd->oc)
+ {
+ case OC_END:
+ result = GNUNET_OK;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ case OC_ADMIN_ADD_INCOMING:
+ if (NULL !=
+ cmd->details.admin_add_incoming.reserve_reference)
+ {
+ ref = find_command (is,
+ cmd->details.admin_add_incoming.reserve_reference);
+ GNUNET_assert (NULL != ref);
+ GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
+ cmd->details.admin_add_incoming.reserve_priv
+ = ref->details.admin_add_incoming.reserve_priv;
+ }
+ else
+ {
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv;
+ GNUNET_free (priv);
+ }
+ GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv,
+ &reserve_pub.eddsa_pub);
+ if (GNUNET_OK !=
+ TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
+ &amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at %u\n",
+ cmd->details.admin_add_incoming.amount,
+ is->ip);
+ fail (is);
+ return;
+ }
+ wire = json_loads (cmd->details.admin_add_incoming.wire,
+ JSON_REJECT_DUPLICATES,
+ NULL);
+ if (NULL == wire)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse wire details `%s' at %u\n",
+ cmd->details.admin_add_incoming.wire,
+ is->ip);
+ fail (is);
+ return;
+ }
+ execution_date = GNUNET_TIME_absolute_get ();
+ TALER_round_abs_time (&execution_date);
+ cmd->details.admin_add_incoming.aih
+ = TALER_MINT_admin_add_incoming (mint,
+ &reserve_pub,
+ &amount,
+ execution_date,
+ wire,
+ &add_incoming_cb,
+ is);
+ if (NULL == cmd->details.admin_add_incoming.aih)
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ trigger_context_task ();
+ return;
+ case OC_WITHDRAW_STATUS:
+ GNUNET_assert (NULL !=
+ cmd->details.reserve_status.reserve_reference);
+ ref = find_command (is,
+ cmd->details.reserve_status.reserve_reference);
+ GNUNET_assert (NULL != ref);
+ GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
+ GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.admin_add_incoming.reserve_priv.eddsa_priv,
+ &reserve_pub.eddsa_pub);
+ cmd->details.reserve_status.wsh
+ = TALER_MINT_reserve_status (mint,
+ &reserve_pub,
+ &reserve_status_cb,
+ is);
+ trigger_context_task ();
+ return;
+ case OC_WITHDRAW_SIGN:
+ GNUNET_assert (NULL !=
+ cmd->details.reserve_withdraw.reserve_reference);
+ ref = find_command (is,
+ cmd->details.reserve_withdraw.reserve_reference);
+ GNUNET_assert (NULL != ref);
+ GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
+ if (NULL != cmd->details.reserve_withdraw.amount)
+ {
+ if (GNUNET_OK !=
+ TALER_string_to_amount (cmd->details.reserve_withdraw.amount,
+ &amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at %u\n",
+ cmd->details.reserve_withdraw.amount,
+ is->ip);
+ fail (is);
+ return;
+ }
+ cmd->details.reserve_withdraw.pk = find_pk (is->keys,
+ &amount);
+ }
+ if (NULL == cmd->details.reserve_withdraw.pk)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to determine denomination key at %u\n",
+ is->ip);
+ fail (is);
+ return;
+ }
+
+ /* create coin's private key */
+ {
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ cmd->details.reserve_withdraw.coin_priv.eddsa_priv = *priv;
+ GNUNET_free (priv);
+ }
+ GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.reserve_withdraw.coin_priv.eddsa_priv,
+ &coin_pub.eddsa_pub);
+ cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key
+ = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.reserve_withdraw.pk->key.rsa_public_key));
+ cmd->details.reserve_withdraw.wsh
+ = TALER_MINT_reserve_withdraw (mint,
+ cmd->details.reserve_withdraw.pk,
+ &ref->details.admin_add_incoming.reserve_priv,
+ &cmd->details.reserve_withdraw.coin_priv,
+ &cmd->details.reserve_withdraw.blinding_key,
+ &reserve_withdraw_cb,
+ is);
+ if (NULL == cmd->details.reserve_withdraw.wsh)
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ trigger_context_task ();
+ return;
+ case OC_PAY:
+ {
+ GNUNET_break (0); // FIXME: not implemented!
+ trigger_context_task ();
+ return;
+ }
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unknown instruction %d at %u (%s)\n",
+ cmd->oc,
+ is->ip,
+ cmd->label);
+ fail (is);
+ return;
+ }
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
+ * Function run when the test terminates (good or bad).
+ * Cleans up our state.
+ *
+ * @param cls the interpreter state.
+ * @param tc unused
+ */
+static void
+do_shutdown (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd;
+ unsigned int i;
+
+ shutdown_task = NULL;
+ for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
+ {
+ switch (cmd->oc)
+ {
+ case OC_END:
+ GNUNET_assert (0);
+ break;
+ case OC_ADMIN_ADD_INCOMING:
+ if (NULL != cmd->details.admin_add_incoming.aih)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ i,
+ cmd->label);
+ TALER_MINT_admin_add_incoming_cancel (cmd->details.admin_add_incoming.aih);
+ cmd->details.admin_add_incoming.aih = NULL;
+ }
+ break;
+ case OC_WITHDRAW_STATUS:
+ if (NULL != cmd->details.reserve_status.wsh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ i,
+ cmd->label);
+ TALER_MINT_reserve_status_cancel (cmd->details.reserve_status.wsh);
+ cmd->details.reserve_status.wsh = NULL;
+ }
+ break;
+ case OC_WITHDRAW_SIGN:
+ if (NULL != cmd->details.reserve_withdraw.wsh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ i,
+ cmd->label);
+ TALER_MINT_reserve_withdraw_cancel (cmd->details.reserve_withdraw.wsh);
+ cmd->details.reserve_withdraw.wsh = NULL;
+ }
+ if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature)
+ {
+ GNUNET_CRYPTO_rsa_signature_free (cmd->details.reserve_withdraw.sig.rsa_signature);
+ cmd->details.reserve_withdraw.sig.rsa_signature = NULL;
+ }
+ if (NULL != cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key)
+ {
+ GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key);
+ cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key = NULL;
+ }
+ break;
+ case OC_PAY:
+ GNUNET_break (0); // FIXME: not implemented
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unknown instruction %d at %u (%s)\n",
+ cmd->oc,
+ i,
+ cmd->label);
+ break;
+ }
+ }
+ if (NULL != is->task)
+ {
+ GNUNET_SCHEDULER_cancel (is->task);
+ is->task = NULL;
+ }
+ GNUNET_free (is);
+ if (NULL != ctx_task)
+ {
+ GNUNET_SCHEDULER_cancel (ctx_task);
+ ctx_task = NULL;
+ }
+ if (NULL != mint)
+ {
+ TALER_MINT_disconnect (mint);
+ mint = NULL;
+ }
+ if (NULL != ctx)
+ {
+ TALER_MINT_fini (ctx);
+ ctx = NULL;
+ }
+}
+
+
+/**
+ * Functions of this type are called to provide the retrieved signing and
+ * denomination keys of the mint. No TALER_MINT_*() functions should be called
+ * in this callback.
+ *
+ * @param cls closure
+ * @param keys information about keys of the mint
+ */
+static void
+cert_cb (void *cls,
+ const struct TALER_MINT_Keys *keys)
+{
+ struct InterpreterState *is = cls;
+
+ /* check that keys is OK */
+#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); GNUNET_SCHEDULER_shutdown(); return; } while (0)
+ ERR (NULL == keys);
+ ERR (0 == keys->num_sign_keys);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Read %u signing keys\n",
+ keys->num_sign_keys);
+ ERR (0 == keys->num_denom_keys);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Read %u denomination keys\n",
+ keys->num_denom_keys);
+#undef ERR
+
+ /* run actual tests via interpreter-loop */
+ is->keys = keys;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
+ * Task that runs the context's event loop with the GNUnet scheduler.
+ *
+ * @param cls unused
+ * @param tc scheduler context (unused)
+ */
+static void
+context_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ long timeout;
+ int max_fd;
+ fd_set read_fd_set;
+ fd_set write_fd_set;
+ fd_set except_fd_set;
+ struct GNUNET_NETWORK_FDSet *rs;
+ struct GNUNET_NETWORK_FDSet *ws;
+ struct GNUNET_TIME_Relative delay;
+
+ ctx_task = NULL;
+ TALER_MINT_perform (ctx);
+ max_fd = -1;
+ timeout = -1;
+ FD_ZERO (&read_fd_set);
+ FD_ZERO (&write_fd_set);
+ FD_ZERO (&except_fd_set);
+ TALER_MINT_get_select_info (ctx,
+ &read_fd_set,
+ &write_fd_set,
+ &except_fd_set,
+ &max_fd,
+ &timeout);
+ if (timeout >= 0)
+ delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ timeout);
+ else
+ delay = GNUNET_TIME_UNIT_FOREVER_REL;
+ rs = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (rs,
+ &read_fd_set,
+ max_fd + 1);
+ ws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (ws,
+ &write_fd_set,
+ max_fd + 1);
+ ctx_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ delay,
+ rs,
+ ws,
+ &context_task,
+ cls);
+ GNUNET_NETWORK_fdset_destroy (rs);
+ GNUNET_NETWORK_fdset_destroy (ws);
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct InterpreterState *is;
+ static struct Command commands[] =
+ {
+ /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
+ { .oc = OC_ADMIN_ADD_INCOMING,
+ .label = "create-reserve-1",
+ .expected_response_code = MHD_HTTP_OK,
+ .details.admin_add_incoming.wire = "{ \"type\":\"TEST\", \"bank\":\"source bank\", \"account\":42 }",
+ .details.admin_add_incoming.amount = "EUR:5.01" },
+ /* Withdraw a 5 EUR coin, at fee of 1 ct */
+ { .oc = OC_WITHDRAW_SIGN,
+ .label = "withdraw-coin-1",
+ .expected_response_code = MHD_HTTP_OK,
+ .details.reserve_withdraw.reserve_reference = "create-reserve-1",
+ .details.reserve_withdraw.amount = "EUR:5" },
+ /* Check that deposit and withdraw operation are in history, and
+ that the balance is now at zero */
+ { .oc = OC_WITHDRAW_STATUS,
+ .label = "withdraw-status-1",
+ .expected_response_code = MHD_HTTP_OK,
+ .details.reserve_status.reserve_reference = "create-reserve-1",
+ .details.reserve_status.expected_balance = "EUR:0" },
+ /* Try to pay with the 5 EUR coin (in full) */
+ { .oc = OC_PAY,
+ .label = "deposit-simple",
+ .expected_response_code = MHD_HTTP_OK,
+ .details.deposit.amount = "EUR:5",
+ .details.deposit.coin_ref = "withdraw-coin-1",
+ .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
+ .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
+ .details.deposit.transaction_id = 1 },
+
+ /* Try to double-spend the 5 EUR coin with different wire details */
+ { .oc = OC_PAY,
+ .label = "deposit-double-1",
+ .expected_response_code = MHD_HTTP_FORBIDDEN,
+ .details.deposit.amount = "EUR:5",
+ .details.deposit.coin_ref = "withdraw-coin-1",
+ .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":43 }",
+ .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
+ .details.deposit.transaction_id = 1 },
+ /* Try to double-spend the 5 EUR coin at the same merchant (but different
+ transaction ID) */
+ { .oc = OC_PAY,
+ .label = "deposit-double-2",
+ .expected_response_code = MHD_HTTP_FORBIDDEN,
+ .details.deposit.amount = "EUR:5",
+ .details.deposit.coin_ref = "withdraw-coin-1",
+ .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
+ .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
+ .details.deposit.transaction_id = 2 },
+ /* Try to double-spend the 5 EUR coin at the same merchant (but different
+ contract) */
+ { .oc = OC_PAY,
+ .label = "deposit-double-3",
+ .expected_response_code = MHD_HTTP_FORBIDDEN,
+ .details.deposit.amount = "EUR:5",
+ .details.deposit.coin_ref = "withdraw-coin-1",
+ .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
+ .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":2 } }",
+ .details.deposit.transaction_id = 1 },
+
+ { .oc = OC_END }
+ };
+
+ is = GNUNET_new (struct InterpreterState);
+ is->commands = commands;
+
+ ctx = TALER_MINT_init ();
+ GNUNET_assert (NULL != ctx);
+ ctx_task = GNUNET_SCHEDULER_add_now (&context_task,
+ ctx);
+ mint = TALER_MINT_connect (ctx,
+ "http://localhost:8081",
+ &cert_cb, is,
+ TALER_MINT_OPTION_END);
+ GNUNET_assert (NULL != mint);
+ shutdown_task
+ = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 150),
+ &do_shutdown, is);
+}
+
+
+/**
+ * Main function for the testcase for the mint API.
+ *
+ * @param argc expected to be 1
+ * @param argv expected to only contain the program name
+ */
+int
+main (int argc,
+ char * const *argv)
+{
+ struct GNUNET_OS_Process *proc;
+ struct GNUNET_OS_Process *mintd;
+ struct GNUNET_OS_Process *merchantd;
+
+ GNUNET_log_setup ("test-mint-api",
+ "WARNING",
+ NULL);
+ proc = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-mint-keyup",
+ "taler-mint-keyup",
+ "-d", "test-mint-home",
+ "-m", "test-mint-home/master.priv",
+ NULL);
+ GNUNET_OS_process_wait (proc);
+ GNUNET_OS_process_destroy (proc);
+ mintd = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-mint-httpd",
+ "taler-mint-httpd",
+ "-d", "test-mint-home",
+ NULL);
+ merchantd = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-merchant-httpd",
+ "taler-merchant-httpd",
+ "-c", "test-merchant-home",
+ NULL);
+ /* give child time to start and bind against the socket */
+ fprintf (stderr, "Waiting for taler-mint-httpd to be ready");
+ do
+ {
+ fprintf (stderr, ".");
+ sleep (1);
+ }
+ while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null"));
+ fprintf (stderr, "\n");
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_run (&run, NULL);
+ GNUNET_OS_process_kill (mintd,
+ SIGTERM);
+ GNUNET_OS_process_wait (mintd);
+ GNUNET_OS_process_destroy (mintd);
+ return (GNUNET_OK == result) ? 0 : 1;
+}
+
+/* end of test_merchant_api.c */
diff --git a/src/merchant/.gitignore b/src/merchant/.gitignore
deleted file mode 100644
index ebe41680..00000000
--- a/src/merchant/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-taler-merchant-*
diff --git a/src/merchant/Makefile.am b/src/merchant/Makefile.am
deleted file mode 100644
index fbe80e0a..00000000
--- a/src/merchant/Makefile.am
+++ /dev/null
@@ -1,55 +0,0 @@
-AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
-
-MERCHANT_DB = merchant_db.c merchant_db.h
-bin_PROGRAMS = \
- taler-merchant-dbinit \
- taler-merchant-serve
-
-taler_merchant_dbinit_SOURCES = \
- taler_merchant_dbinit.c \
- $(MERCHANT_DB)
-taler_merchant_dbinit_LDADD = \
- -lpq \
- -lgnunetutil \
- -ltalerutil \
- -ltalerpq \
- -lgnunetpostgres
-taler_merchant_dbinit_LDFLAGS = \
- $(POSTGRESQL_LDFLAGS)
-
-check_PROGRAMS = \
- test-merchant \
- test-merchant-db
-
-test_merchant_SOURCES = \
- merchant.c \
- test_merchant.c
-test_merchant_LDADD = \
- -ltalerutil \
- -lgnunetutil
-
-test_merchant_db_SOURCES = \
- $(MERCHANT_DB) \
- test_merchant_db.c
-test_merchant_db_LDADD = \
- -ltalerutil \
- -ltalerpq \
- -lgnunetutil \
- -lgnunetpostgres \
- -lpq
-
-taler_merchant_serve_SOURCES = \
- taler_merchant_serve.c \
- merchant.c merchant.h \
- $(MERCHANT_DB)
-taler_merchant_serve_LDADD = \
- -lpq \
- -lgnunetutil \
- -lgnunetpostgres \
- -lmicrohttpd \
- -ltalermint \
- -ltalerutil \
- -ltalerpq \
- -ljansson
-taler_merchant_serve_LDFLAGS = \
- $(POSTGRESQL_LDFLAGS)
diff --git a/src/merchant/merchant.c b/src/merchant/merchant.c
deleted file mode 100644
index f124a030..00000000
--- a/src/merchant/merchant.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant.c
- * @brief Common utility functions for merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include "merchant.h"
-
-
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Parses mints from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of mints upon successful parsing. Will be NULL upon
- * error.
- * @return the number of mints in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_MintInfo **mints)
-{
- char *mints_str;
- char *token_nf; /* do no free (nf) */
- char *mint_section;
- char *mint_hostname;
- char *mint_pubkey_enc;
- struct MERCHANT_MintInfo *r_mints;
- struct MERCHANT_MintInfo mint;
- unsigned long long mint_port;
- unsigned int cnt;
- int OK;
-
- OK = 0;
- mints_str = NULL;
- token_nf = NULL;
- mint_section = NULL;
- mint_hostname = NULL;
- mint_pubkey_enc = NULL;
- r_mints = NULL;
- cnt = 0;
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "merchant",
- "TRUSTED_MINTS",
- &mints_str));
- for (token_nf = strtok (mints_str, " ");
- NULL != token_nf;
- token_nf = strtok (NULL, " "))
- {
- GNUNET_assert (0 < GNUNET_asprintf (&mint_section,
- "mint-%s", token_nf));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- mint_section,
- "HOSTNAME",
- &mint_hostname));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (cfg,
- mint_section,
- "PORT",
- &mint_port));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- mint_section,
- "PUBKEY",
- &mint_pubkey_enc));
- EXITIF (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc,
- strlen (mint_pubkey_enc),
- &mint.pubkey));
- mint.hostname = mint_hostname;
- mint.port = (uint16_t) mint_port;
- GNUNET_array_append (r_mints, cnt, mint);
- mint_hostname = NULL;
- GNUNET_free (mint_pubkey_enc);
- mint_pubkey_enc = NULL;
- GNUNET_free (mint_section);
- mint_section = NULL;
- }
- OK = 1;
-
- EXITIF_exit:
- GNUNET_free_non_null (mints_str);
- GNUNET_free_non_null (mint_section);
- GNUNET_free_non_null (mint_hostname);
- GNUNET_free_non_null (mint_pubkey_enc);
- if (!OK)
- {
- GNUNET_free_non_null (r_mints);
- return GNUNET_SYSERR;
- }
-
- *mints = r_mints;
- return cnt;
-}
-
-
-/**
- * Parse the SEPA information from the configuration. If any of the required
- * fileds is missing return NULL.
- *
- * @param cfg the configuration
- * @return Sepa details as a structure; NULL upon error
- */
-struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- struct MERCHANT_WIREFORMAT_Sepa *wf;
-
- wf = GNUNET_new (struct MERCHANT_WIREFORMAT_Sepa);
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "IBAN",
- &wf->iban));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "NAME",
- &wf->name));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "BIC",
- &wf->bic));
- return wf;
-
- EXITIF_exit:
- GNUNET_free_non_null (wf->iban);
- GNUNET_free_non_null (wf->name);
- GNUNET_free_non_null (wf->bic);
- GNUNET_free (wf);
- return NULL;
-
-}
-
-
-/**
- * Destroy and free resouces occupied by the wireformat structure
- *
- * @param wf the wireformat structure
- */
-void
-TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf)
-{
- GNUNET_free_non_null (wf->iban);
- GNUNET_free_non_null (wf->name);
- GNUNET_free_non_null (wf->bic);
- GNUNET_free (wf);
-}
-
-/* end of merchant.c */
diff --git a/src/merchant/merchant.h b/src/merchant/merchant.h
deleted file mode 100644
index c66131ed..00000000
--- a/src/merchant/merchant.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant.c
- * @brief Common utility functions for merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#ifndef MERCHANT_H
-#define MERCHANT_H
-
-#include <gnunet/gnunet_common.h>
-#include <gnunet/gnunet_crypto_lib.h>
-
-/**
- * A mint
- */
-struct MERCHANT_MintInfo {
- /**
- * Hostname
- */
- char *hostname;
-
- /**
- * The public key of the mint
- */
- struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
-
- /**
- * The port where the mint's service is running
- */
- uint16_t port;
-
-};
-
-
-/**
- * Parses mints from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of mints upon successful parsing. Will be NULL upon
- * error.
- * @return the number of mints in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_MintInfo **mints);
-
-
-GNUNET_NETWORK_STRUCT_BEGIN
-struct MERCHANT_WIREFORMAT_Sepa
-{
- /**
- * The international bank account number
- */
- char *iban;
-
- /**
- * Name of the bank account holder
- */
- char *name;
-
- /**
- *The bank identification code
- */
- char *bic;
-
- /**
- * The latest payout date when the payment corresponding to this account has
- * to take place. A value of 0 indicates a transfer as soon as possible.
- */
- struct GNUNET_TIME_AbsoluteNBO payout;
-};
-GNUNET_NETWORK_STRUCT_END
-
-/**
- * Parse the SEPA information from the configuration. If any of the required
- * fileds is missing return NULL.
- *
- * @param cfg the configuration
- * @return Sepa details as a structure; NULL upon error
- */
-struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-
-/**
- * Destroy and free resouces occupied by the wireformat structure
- *
- * @param wf the wireformat structure
- */
-void
-TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf);
-
-#endif /* MERCHANT_H */
diff --git a/src/merchant/merchant_db.c b/src/merchant/merchant_db.c
deleted file mode 100644
index e87dbcc8..00000000
--- a/src/merchant/merchant_db.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant_db.c
- * @brief database helper functions used by the merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <taler/taler_util.h>
-#include <taler/db_pq.h>
-#include "merchant_db.h"
-
-
-#define PQSQL_strerror(kind, cmd, res) \
- GNUNET_log_from (kind, "merchant-db", \
- "SQL %s failed at %s:%u with error: %s", \
- cmd, __FILE__, __LINE__, PQresultErrorMessage (res));
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Connect to postgresql database
- *
- * @param cfg the configuration handle
- * @return connection to the postgresql database; NULL upon error
- */
-PGconn *
-MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- return GNUNET_POSTGRES_connect (cfg, "merchant-db");
-}
-
-
-/**
- * Disconnect from the database
- *
- * @param conn database handle to close
- */
-void
-MERCHANT_DB_disconnect (PGconn *conn)
-{
- PQfinish (conn);
-}
-
-
-/**
- * Initialise merchant tables
- *
- * @param conn the connection handle to postgres db.
- * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
- * contents are dropped when the @a conn is closed
- * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
- */
-int
-MERCHANT_DB_initialise (PGconn *conn, int tmp)
-{
- const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
- char *sql;
- PGresult *res;
- ExecStatusType status;
- int ret;
-
- res = NULL;
- (void) GNUNET_asprintf (&sql,
- "BEGIN TRANSACTION;"
- "CREATE %1$s TABLE IF NOT EXISTS contracts ("
- "transaction_id SERIAL8 PRIMARY KEY,"
- "amount INT4 NOT NULL,"
- "amount_fraction INT4 NOT NULL,"
- "description TEXT NOT NULL,"
- "nounce BYTEA NOT NULL,"
- "expiry INT8 NOT NULL,"
- "product INT8 NOT NULL);"
- "CREATE %1$s TABLE IF NOT EXISTS checkouts ("
- "coin_pub BYTEA PRIMARY KEY,"
- "transaction_id INT8 REFERENCES contracts(transaction_id),"
- "amount INT4 NOT NULL,"
- "amount_fraction INT4 NOT NULL,"
- "coin_sig BYTEA NOT NULL);",
- tmp_str);
- ret = GNUNET_POSTGRES_exec (conn, sql);
- (void) GNUNET_POSTGRES_exec (conn,
- (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK");
- GNUNET_free (sql);
- if (GNUNET_OK != ret)
- return ret;
-
- while (NULL != (res = PQgetResult (conn)))
- {
- status = PQresultStatus (res);
- PQclear (res);
- }
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "contract_create",
- "INSERT INTO contracts"
- "(amount, amount_fraction, description,"
- "nounce, expiry, product) VALUES"
- "($1, $2, $3, $4, $5, $6)"
- "RETURNING transaction_id",
- 6, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "get_contract_product",
- "SELECT ("
- "product"
- ") FROM contracts "
- "WHERE ("
- "transaction_id=$1"
- ")",
- 1, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "checkout_create",
- "INSERT INTO checkouts ("
- "coin_pub,"
- "transaction_id,"
- "amount,"
- "amount_fraction,"
- "coin_sig"
- ") VALUES ("
- "$1, $2, $3, $4, $5"
- ")",
- 5, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "get_checkout_product",
- "SELECT ("
- "product"
- ") FROM contracts "
- "WHERE "
- "transaction_id IN ("
- "SELECT (transaction_id) FROM checkouts "
- "WHERE coin_pub=$1"
- ")",
- 1, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
- PQclear (res);
-
- return GNUNET_OK;
-
- EXITIF_exit:
- if (NULL != res)
- {
- PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res);
- PQclear (res);
- }
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Inserts a contract record into the database and if successfull returns the
- * serial number of the inserted row.
- *
- * @param conn the database connection
- * @param expiry the time when the contract will expire
- * @param amount the taler amount corresponding to the contract
- * @param desc descripition of the contract
- * @param nounce a random 64-bit nounce
- * @param product description to identify a product
- * @return -1 upon error; the serial id of the inserted contract upon success
- */
-long long
-MERCHANT_DB_contract_create (PGconn *conn,
- struct GNUNET_TIME_Absolute expiry,
- struct TALER_Amount *amount,
- const char *desc,
- uint64_t nounce,
- uint64_t product)
-{
- PGresult *res;
- uint64_t expiry_ms_nbo;
- uint32_t value_nbo;
- uint32_t fraction_nbo;
- uint64_t nounce_nbo;
- ExecStatusType status;
- long long id;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (&value_nbo),
- TALER_DB_QUERY_PARAM_PTR (&fraction_nbo),
- TALER_DB_QUERY_PARAM_PTR_SIZED (desc, strlen(desc)),
- TALER_DB_QUERY_PARAM_PTR (&nounce_nbo),
- TALER_DB_QUERY_PARAM_PTR (&expiry_ms_nbo),
- TALER_DB_QUERY_PARAM_PTR (&product),
- TALER_DB_QUERY_PARAM_END
- };
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC("transaction_id", &id),
- TALER_DB_RESULT_SPEC_END
- };
-
- expiry_ms_nbo = GNUNET_htonll (expiry.abs_value_us);
- value_nbo = htonl (amount->value);
- fraction_nbo = htonl (amount->fraction);
- nounce_nbo = GNUNET_htonll (nounce);
- product = GNUNET_htonll (product);
- res = TALER_DB_exec_prepared (conn, "contract_create", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_DB_extract_result (res, rs, 0));
- PQclear (res);
- return GNUNET_ntohll ((uint64_t) id);
-
- EXITIF_exit:
- PQclear (res);
- return -1;
-}
-
-long long
-MERCHANT_DB_get_contract_product (PGconn *conn,
- uint64_t contract_id)
-{
- PGresult *res;
- int64_t product;
- ExecStatusType status;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (&contract_id),
- TALER_DB_QUERY_PARAM_END
- };
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC("product", &product),
- TALER_DB_RESULT_SPEC_END
- };
-
- contract_id = GNUNET_htonll (contract_id);
- res = TALER_DB_exec_prepared (conn, "get_contract_product", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_DB_extract_result (res, rs, 0));
- PQclear (res);
- return GNUNET_ntohll ((uint64_t) product);
-
- EXITIF_exit:
- PQclear (res);
- return -1;
-}
-
-unsigned int
-MERCHANT_DB_checkout_create (PGconn *conn,
- struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub,
- uint64_t transaction_id,
- struct TALER_Amount *amount,
- struct GNUNET_CRYPTO_EddsaSignature *coin_sig)
-{
- PGresult *res;
- ExecStatusType status;
- uint32_t value_nbo;
- uint32_t fraction_nbo;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (coin_pub),
- TALER_DB_QUERY_PARAM_PTR (&transaction_id),
- TALER_DB_QUERY_PARAM_PTR (&value_nbo),
- TALER_DB_QUERY_PARAM_PTR (&fraction_nbo),
- TALER_DB_QUERY_PARAM_PTR (coin_sig),
- TALER_DB_QUERY_PARAM_END
- };
-
- transaction_id = GNUNET_htonll (transaction_id);
- value_nbo = htonl (amount->value);
- fraction_nbo = htonl (amount->fraction);
- res = TALER_DB_exec_prepared (conn, "checkout_create", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_COMMAND_OK != status);
- PQclear (res);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (res);
- return GNUNET_SYSERR;
-}
-
-
-long long
-MERCHANT_DB_get_checkout_product (PGconn *conn,
- struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub)
-{
- PGresult *res;
- ExecStatusType status;
- uint64_t product;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (coin_pub),
- TALER_DB_QUERY_PARAM_END
- };
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC("product", &product),
- TALER_DB_RESULT_SPEC_END
- };
-
- product = -1;
- res = TALER_DB_exec_prepared (conn, "get_checkout_product", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- if (0 == PQntuples (res))
- {
- char *coin_pub_enc;
- coin_pub_enc = GNUNET_CRYPTO_eddsa_public_key_to_string (coin_pub);
- LOG_DEBUG ("Checkout not found for given coin: %s\n",
- coin_pub_enc);
- GNUNET_free (coin_pub_enc);
- goto EXITIF_exit;
- }
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_DB_extract_result (res, rs, 0));
- PQclear (res);
- return GNUNET_ntohll ((uint64_t) product);
-
- EXITIF_exit:
- PQclear (res);
- return -1;
-}
-/* end of merchant-db.c */
diff --git a/src/merchant/merchant_db.h b/src/merchant/merchant_db.h
deleted file mode 100644
index 734d547f..00000000
--- a/src/merchant/merchant_db.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/merchant_db.h
- * @brief database helper functions used by the merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#ifndef MERCHANT_DB_H
-#define MERCHANT_DB_H
-
-#include <gnunet/gnunet_postgres_lib.h>
-#include <taler/taler_util.h>
-
-/**
- * Connect to postgresql database
- *
- * @param cfg the configuration handle
- * @return connection to the postgresql database; NULL upon error
- */
-PGconn *
-MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-
-/**
- * Disconnect from the database
- *
- * @param conn database handle to close
- */
-void
-MERCHANT_DB_disconnect (PGconn *conn);
-
-
-/**
- * Initialise merchant tables
- *
- * @param conn the connection handle to postgres db.
- * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
- * contents are dropped when the @a conn is closed
- * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
- */
-int
-MERCHANT_DB_initialise (PGconn *conn, int tmp);
-
-
-/**
- * Inserts a contract record into the database and if successfull returns the
- * serial number of the inserted row.
- *
- * @param conn the database connection
- * @param expiry the time when the contract will expire
- * @param amount the taler amount corresponding to the contract
- * @param desc descripition of the contract
- * @param nounce a random 64-bit nounce
- * @param product description to identify a product
- * @return -1 upon error; the serial id of the inserted contract upon success
- */
-long long
-MERCHANT_DB_contract_create (PGconn *conn,
- struct GNUNET_TIME_Absolute expiry,
- struct TALER_Amount *amount,
- const char *desc,
- uint64_t nounce,
- uint64_t product);
-
-long long
-MERCHANT_DB_get_contract_product (PGconn *conn,
- uint64_t contract_id);
-
-unsigned int
-MERCHANT_DB_checkout_create (PGconn *conn,
- struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub,
- uint64_t transaction_id,
- struct TALER_Amount *amount,
- struct GNUNET_CRYPTO_EddsaSignature *coin_sig);
-
-
-long long
-MERCHANT_DB_get_checkout_product (PGconn *conn,
- struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub);
-
-#endif /* MERCHANT_DB_H */
-
-/* end of merchant-db.h */
diff --git a/src/merchant/taler_merchant_dbinit.c b/src/merchant/taler_merchant_dbinit.c
deleted file mode 100644
index e6d0af9d..00000000
--- a/src/merchant/taler_merchant_dbinit.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/taler_merchant_dbinit.c
- * @brief Program to initialise merchant database
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include "merchant_db.h"
-
-
-/**
- * Global execution result
- */
-static int result;
-
-
-/**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param config configuration
- */
-static void
-run (void *cls, char *const *args, const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *config)
-{
- PGconn *conn;
-
- conn = MERCHANT_DB_connect (config);
- if (NULL == conn)
- return;
- if (GNUNET_OK == MERCHANT_DB_initialise (conn, GNUNET_NO))
- result = GNUNET_OK;
- MERCHANT_DB_disconnect (conn);
-}
-
-
-/**
- * The main function
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc, char *const *argv)
-{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_END
- };
-
- result = GNUNET_SYSERR;
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run (argc, argv, "taler-merchant-dbinit",
- gettext_noop
- ("Initialise Taler Merchant's database"),
- options, &run, NULL))
- return 3;
- return (GNUNET_OK == result) ? 0 : 1;
-}
diff --git a/src/merchant/taler_merchant_serve.c b/src/merchant/taler_merchant_serve.c
deleted file mode 100644
index 0f00a8c2..00000000
--- a/src/merchant/taler_merchant_serve.c
+++ /dev/null
@@ -1,1539 +0,0 @@
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <microhttpd.h>
-#include <taler/taler_util.h>
-#include "merchant.h"
-#include "merchant_db.h"
-#include <taler/taler_mint_service.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_json_lib.h>
-
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-/**
- * Shorthand for exit jumps due to protocol exceptions resulting from client's
- * mistakes
- */
-#define EXITIF_OP(cond) \
- do { \
- if (cond) { GNUNET_break_op (0); goto EXITIF_exit; } \
- } while (0)
-
-/**
- * Print JSON parsing related error information
- */
-#define WARN_JSON(error) \
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
- "JSON parsing failed at %s:%u: %s (%s)", \
- __FILE__, __LINE__, error.text, error.source)
-
-/**
- * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
- */
-#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000)
-
-
-struct ContractData
-{
- char *product;
-};
-
-
-GNUNET_NETWORK_STRUCT_BEGIN
-
-struct Contract
-{
- /**
- * The signature of the merchant for this contract
- */
- struct GNUNET_CRYPTO_EddsaSignature sig;
-
- /**
- * Purpose header for the signature over contract
- */
- struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
-
- /**
- * The transaction identifier
- */
- char m[13];
-
- /**
- * Expiry time
- */
- struct GNUNET_TIME_AbsoluteNBO t;
-
- /**
- * The invoice amount
- */
- struct TALER_AmountNBO amount;
-
- /**
- * The hash of the preferred wire format + nounce
- */
- struct GNUNET_HashCode h_wire;
-
- /**
- * The contract data
- */
- char a[];
-};
-
-GNUNET_NETWORK_STRUCT_END
-
-/**
- * A download object
- */
-struct Download {
- struct Download *next;
- struct Download *prev;
- char *filename;
- struct MHD_Response *resp;
- unsigned int id;
-};
-
-/**
- * DLL for downloadable objects
- */
-struct Download *dwn_head;
-struct Download *dwn_tail;
-
-/**
- * MHD response object for listing all products
- */
-struct MHD_Response *list_products_resp;
-
-/**
- * Number of files we make available for downloading
- */
-static unsigned int ndownloads;
-
-
-/**
- * Context information of the mints we trust
- */
-struct Mint
-{
- /**
- * Public key of this mint
- */
- struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
-
- /**
- * Connection handle to this mint
- */
- struct TALER_MINT_Handle *conn;
-};
-
-/**
- * Hashmap to store the mint context information
- */
-static struct GNUNET_CONTAINER_MultiPeerMap *mints_map;
-
-/**
- * Our private key
- */
-struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
-
-/**
- * Connection handle to the our database
- */
-PGconn *db_conn;
-
-/**
- * The MHD Daemon
- */
-static struct MHD_Daemon *mhd;
-
-/**
- * Our wireformat
- */
-static struct MERCHANT_WIREFORMAT_Sepa *wire;
-
-/**
- * Hash of the wireformat
- */
-static struct GNUNET_HashCode h_wire;
-
-/**
- * Shutdown task identifier
- */
-static struct GNUNET_SCHEDULER_Task *shutdown_task;
-
-/**
- * Task for calling the select on MHD's sockets
- */
-static struct GNUNET_SCHEDULER_Task *select_task;
-
-/**
- * The port we are running on
- */
-static long long unsigned port;
-
-/**
- * Mint context
- */
-static struct TALER_MINT_Context *mctx;
-
-/**
- * Our hostname
- */
-static char *hostname;
-
-/**
- * Directory of data items to serve
- */
-static char *data_dir;
-
-/**
- * Should we do a dry run where temporary tables are used for storing the data.
- */
-static int dry;
-
-/**
- * Global return code
- */
-static int result;
-
-
-
-/**
- * Send JSON object as response. Decreases the reference count of the
- * JSON object.
- *
- * @param connection the MHD connection
- * @param json the json object
- * @param status_code the http status code
- * @return MHD result code
- */
-static int
-send_response_json (struct MHD_Connection *connection,
- json_t *json,
- unsigned int status_code)
-{
- struct MHD_Response *resp;
- char *json_str;
-
- json_str = json_dumps (json, JSON_INDENT(2));
- json_decref (json);
- resp = MHD_create_response_from_buffer (strlen (json_str), json_str,
- MHD_RESPMEM_MUST_FREE);
- if (NULL == resp)
- return MHD_NO;
- return MHD_queue_response (connection, status_code, resp);
-}
-
-
-/* ************ JSON post-processing logic; FIXME: why do we use JSON here!? ********** */
-
-
-/**
- * Initial size for POST
- * request buffer.
- */
-#define REQUEST_BUFFER_INITIAL 1024
-
-/**
- * Maximum POST request size
- */
-#define REQUEST_BUFFER_MAX (1024*1024)
-
-
-/**
- * Buffer for POST requests.
- */
-struct Buffer
-{
- /**
- * Allocated memory
- */
- char *data;
-
- /**
- * Number of valid bytes in buffer.
- */
- size_t fill;
-
- /**
- * Number of allocated bytes in buffer.
- */
- size_t alloc;
-};
-
-
-/**
- * Initialize a buffer.
- *
- * @param buf the buffer to initialize
- * @param data the initial data
- * @param data_size size of the initial data
- * @param alloc_size size of the buffer
- * @param max_size maximum size that the buffer can grow to
- * @return a GNUnet result code
- */
-static int
-buffer_init (struct Buffer *buf, const void *data, size_t data_size, size_t alloc_size, size_t max_size)
-{
- if (data_size > max_size || alloc_size > max_size)
- return GNUNET_SYSERR;
- if (data_size > alloc_size)
- alloc_size = data_size;
- buf->data = GNUNET_malloc (alloc_size);
- memcpy (buf->data, data, data_size);
- return GNUNET_OK;
-}
-
-
-/**
- * Free the data in a buffer. Does *not* free
- * the buffer object itself.
- *
- * @param buf buffer to de-initialize
- */
-static void
-buffer_deinit (struct Buffer *buf)
-{
- GNUNET_free (buf->data);
- buf->data = NULL;
-}
-
-
-/**
- * Append data to a buffer, growing the buffer if necessary.
- *
- * @param buf the buffer to append to
- * @param data the data to append
- * @param size the size of @a data
- * @param max_size maximum size that the buffer can grow to
- * @return GNUNET_OK on success,
- * GNUNET_NO if the buffer can't accomodate for the new data
- * GNUNET_SYSERR on fatal error (out of memory?)
- */
-static int
-buffer_append (struct Buffer *buf, const void *data, size_t data_size, size_t max_size)
-{
- if (buf->fill + data_size > max_size)
- return GNUNET_NO;
- if (data_size + buf->fill > buf->alloc)
- {
- char *new_buf;
- size_t new_size = buf->alloc;
- while (new_size < buf->fill + data_size)
- new_size += 2;
- if (new_size > max_size)
- return GNUNET_NO;
- new_buf = GNUNET_malloc (new_size);
- memcpy (new_buf, buf->data, buf->fill);
- buf->data = new_buf;
- buf->alloc = new_size;
- }
- memcpy (buf->data + buf->fill, data, data_size);
- buf->fill += data_size;
- return GNUNET_OK;
-}
-
-
-
-/**
- * Process a POST request containing a JSON object.
- *
- * @param connection the MHD connection
- * @param con_cs the closure (contains a 'struct Buffer *')
- * @param upload_data the POST data
- * @param upload_data_size the POST data size
- * @param json the JSON object for a completed request
- *
- * @returns
- * GNUNET_YES if json object was parsed
- * GNUNET_NO is request incomplete or invalid
- * GNUNET_SYSERR on internal error
- */
-static int
-process_post_json (struct MHD_Connection *connection,
- void **con_cls,
- const char *upload_data,
- size_t *upload_data_size,
- json_t **json)
-{
- struct Buffer *r = *con_cls;
-
- if (NULL == *con_cls)
- {
- /* We are seeing a fresh POST request. */
-
- r = GNUNET_new (struct Buffer);
- if (GNUNET_OK != buffer_init (r, upload_data, *upload_data_size,
- REQUEST_BUFFER_INITIAL, REQUEST_BUFFER_MAX))
- {
- *con_cls = NULL;
- buffer_deinit (r);
- GNUNET_free (r);
- return GNUNET_SYSERR;
- }
- *upload_data_size = 0;
- *con_cls = r;
- return GNUNET_NO;
- }
- if (0 != *upload_data_size)
- {
- /* We are seeing an old request with more data available. */
-
- if (GNUNET_OK != buffer_append (r, upload_data, *upload_data_size,
- REQUEST_BUFFER_MAX))
- {
- /* Request too long or we're out of memory. */
-
- *con_cls = NULL;
- buffer_deinit (r);
- GNUNET_free (r);
- return GNUNET_SYSERR;
- }
- *upload_data_size = 0;
- return GNUNET_NO;
- }
-
- /* We have seen the whole request. */
-
- *json = json_loadb (r->data, r->fill, 0, NULL);
- buffer_deinit (r);
- GNUNET_free (r);
- if (NULL == *json)
- {
- struct MHD_Response *resp;
- int ret;
-
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Can't parse JSON request body\n");
- resp = MHD_create_response_from_buffer (strlen ("parse error"),
- "parse error",
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_BAD_REQUEST,
- resp);
- MHD_destroy_response (resp);
- return ret;
- }
- *con_cls = NULL;
-
- return GNUNET_YES;
-}
-
-
-/* ************** END of JSON POST processing logic ************ */
-
-
-static struct GNUNET_HashCode
-hash_wireformat (uint64_t nounce)
-{
- struct GNUNET_HashContext *hc;
- struct GNUNET_HashCode hash;
-
- hc = GNUNET_CRYPTO_hash_context_start ();
- GNUNET_CRYPTO_hash_context_read (hc, wire->iban, strlen (wire->iban));
- GNUNET_CRYPTO_hash_context_read (hc, wire->name, strlen (wire->name));
- GNUNET_CRYPTO_hash_context_read (hc, wire->bic, strlen (wire->bic));
- nounce = GNUNET_htonll (nounce);
- GNUNET_CRYPTO_hash_context_read (hc, &nounce, sizeof (nounce));
- GNUNET_CRYPTO_hash_context_finish (hc, &hash);
- return hash;
-}
-
-
-static json_t *
-build_json_contract (struct Contract *contract)
-{
- return json_pack ("{s:s, s:o, s:o, s:s, s:o, s:o}",
- "transaction_id", contract->m,
- "expiry", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (contract->t)),
- "amount", TALER_JSON_from_amount (TALER_amount_ntoh (contract->amount)),
- "description", contract->a,
- "H_wire", TALER_JSON_from_data (&contract->h_wire, sizeof (struct GNUNET_HashCode)),
- "msig", TALER_JSON_from_data (&contract->sig, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
-}
-
-/**
- * Cleeanup entries in the peer map.
- *
- * @param cls closure
- * @param key current public key
- * @param value value in the hash map
- * @return #GNUNET_YES if we should continue to
- * iterate,
- * #GNUNET_NO if not.
- */
-static int
-mints_cleanup_iterator (void *cls,
- const struct GNUNET_PeerIdentity *key,
- void *value)
-{
- struct Mint *mint = value;
-
- if (NULL != mint->conn)
- TALER_MINT_disconnect (mint->conn);
- GNUNET_CONTAINER_multipeermap_remove (mints_map, key, mint);
- GNUNET_free (mint);
- return GNUNET_YES;
-}
-
-
-/**
- * Shutdown task
- *
- * @param cls NULL
- * @param tc scheduler task context
- */
-static void
-do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- struct Download *dwn;
-
- shutdown_task = NULL;
- if (NULL != select_task)
- {
- GNUNET_SCHEDULER_cancel (select_task);
- select_task = NULL;
- }
- if (NULL != list_products_resp)
- {
- MHD_destroy_response (list_products_resp);
- list_products_resp = NULL;
- }
- if (NULL != mhd)
- {
- MHD_stop_daemon (mhd);
- mhd = NULL;
- }
- if (NULL != db_conn)
- {
- MERCHANT_DB_disconnect (db_conn);
- db_conn = NULL;
- }
- if (NULL != mints_map)
- {
- GNUNET_CONTAINER_multipeermap_iterate (mints_map,
- &mints_cleanup_iterator,
- NULL);
- GNUNET_CONTAINER_multipeermap_destroy (mints_map);
- mints_map = NULL;
- }
- if (NULL != mctx)
- {
- TALER_MINT_cleanup (mctx);
- mctx = NULL;
- }
- if (NULL != wire)
- {
- TALER_MERCHANT_destroy_wireformat_sepa (wire);
- wire = NULL;
- }
- while (NULL != (dwn = dwn_head))
- {
- GNUNET_CONTAINER_DLL_remove (dwn_head, dwn_tail, dwn);
- if (NULL != dwn->resp)
- MHD_destroy_response (dwn->resp);
- GNUNET_free (dwn->filename);
- GNUNET_free (dwn);
- }
-}
-
-
-/**
- * Get the MHD's sockets which are to be called with select() and schedule the
- * select task.
- *
- * @return GNUNET_YES upon success; GNUNET_NO upon error, in this case the
- * select task will not be queued.
- */
-static int
-poll_mhd ();
-
-
-/**
- * One of the MHD's sockets are ready. Call MHD_run_from_select ().
- *
- * @param cls NULL
- * @param tc scheduler task context
- */
-static void
-run_mhd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- fd_set fd_rs;
- fd_set fd_ws;
- select_task = NULL;
- if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
- return;
- FD_ZERO (&fd_rs);
- FD_ZERO (&fd_ws);
- if (0 != (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
- fd_rs = tc->read_ready->sds;
- if (0 != (GNUNET_SCHEDULER_REASON_WRITE_READY & tc->reason))
- fd_ws = tc->write_ready->sds;
- EXITIF (MHD_YES != MHD_run_from_select (mhd,
- &fd_rs,
- &fd_ws,
- NULL));
- EXITIF (GNUNET_NO == poll_mhd ());
- return;
-
- EXITIF_exit:
- result = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
-}
-
-
-/**
- * Get the MHD's sockets which are to be called with select() and schedule the
- * select task.
- *
- * @return GNUNET_YES upon success; GNUNET_NO upon error, in this case the
- * select task will not be queued.
- */
-static int
-poll_mhd ()
-{
- struct GNUNET_NETWORK_FDSet rs;
- struct GNUNET_NETWORK_FDSet ws;
- fd_set fd_rs;
- fd_set fd_ws;
- fd_set fd_es;
- struct GNUNET_TIME_Relative delay;
- unsigned long long timeout;
- int max_fd;
-
- FD_ZERO (&fd_rs);
- FD_ZERO (&fd_ws);
- FD_ZERO (&fd_es);
- max_fd = 0;
- if (MHD_YES != MHD_get_fdset (mhd,
- &fd_rs,
- &fd_ws,
- &fd_es,
- &max_fd))
- return GNUNET_SYSERR;
- GNUNET_NETWORK_fdset_zero (&rs);
- GNUNET_NETWORK_fdset_zero (&ws);
- GNUNET_NETWORK_fdset_copy_native (&rs, &fd_rs, max_fd + 1);
- GNUNET_NETWORK_fdset_copy_native (&ws, &fd_ws, max_fd + 1);
- if (MHD_NO == MHD_get_timeout (mhd, &timeout))
- timeout = 0;
- if (0 == timeout)
- delay = GNUNET_TIME_UNIT_FOREVER_REL;
- else
- delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- timeout);
- if (NULL != select_task)
- GNUNET_SCHEDULER_cancel (select_task);
- select_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
- delay,
- &rs,
- &ws,
- &run_mhd,
- NULL);
- return GNUNET_OK;
-}
-
-static int
-failure_resp (struct MHD_Connection *connection, unsigned int status)
-{
- static char page_404[]="\
-<!DOCTYPE html> \
-<html><title>Resource not found</title><body><center> \
-<h3>The resource you are looking for is not found.</h3> \
-</center></body></html>";
- static char page_500[]="\
-<!DOCTYPE html> <html><title>Internal Server Error</title><body><center> \
-<h3>The server experienced an internal error and hence cannot serve your \
-request</h3></center></body></html>";
- struct MHD_Response *resp;
- char *page;
- size_t size;
-#define PAGE(number) \
- do {page=page_ ## number; size=sizeof(page_ ## number)-1;} while(0)
-
- GNUNET_assert (400 <= status);
- resp = NULL;
- switch (status)
- {
- case 404:
- PAGE(404);
- break;
- default:
- status = 500;
- case 500:
- PAGE(500);
- }
-#undef PAGE
-
- EXITIF (NULL == (resp = MHD_create_response_from_buffer (size,
- page,
- MHD_RESPMEM_PERSISTENT)));
- EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
- MHD_destroy_response (resp);
- return GNUNET_OK;
-
- EXITIF_exit:
- if (NULL != resp)
- MHD_destroy_response (resp);
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Iterator over key-value pairs. This iterator
- * can be used to iterate over all of the cookies,
- * headers, or POST-data fields of a request, and
- * also to iterate over the headers that have been
- * added to a response.
- *
- * @param cls closure
- * @param kind kind of the header we are looking at
- * @param key key for the value, can be an empty string
- * @param value corresponding value, can be NULL
- * @return #MHD_YES to continue iterating,
- * #MHD_NO to abort the iteration
- * @ingroup request
- */
-static int
-get_contract_values_iter (void *cls,
- enum MHD_ValueKind kind,
- const char *key, const char *value)
-{
- unsigned long long id;
- uint64_t *product = cls;
-
-#define STR_PRODUCT "product"
- if (0 == strncasecmp (key, STR_PRODUCT, sizeof (STR_PRODUCT) - 1))
- {
- if (1 > sscanf (value, "%llu", &id))
- return GNUNET_NO;
- *product = (uint64_t) id;
- }
- return GNUNET_YES;
-}
-
-#if 0
-static const char *
-uint64_to_enc (uint64_t i)
-{
- static char buf[14];
- i = GNUNET_htonll (i);
- GNUNET_break (NULL !=
- GNUNET_STRINGS_data_to_string (&i, sizeof (i), buf, sizeof (buf)));
- buf[13] = '\0';
- return buf;
-}
-
-static uint64_t
-enc_to_uint64 (const char *enc)
-{
- uint64_t i;
- GNUNET_break (GNUNET_OK ==
- GNUNET_STRINGS_string_to_data (enc, strlen(enc), &i, sizeof
- (i)));
- return GNUNET_ntohll (i);
-}
-#endif
-
-/**
- * Prepare a contract, store it in database and send the corresponding JSON.
- *
- * @param connection MHD connection handle
- * @param _resp pointer to hold the result response upon success
- * @return the status code 200 when a contract is generated; 404 when the
- * product is not found or upon other errors.
- */
-static unsigned int
-handle_get_contract (struct MHD_Connection *connection,
- struct MHD_Response **_resp)
-{
- struct MHD_Response *resp;
- struct Contract *contract;
- struct GNUNET_TIME_Absolute expiry;
- struct TALER_Amount amount;
- char *template = "A contract from GNUnet e.V thanking you for a"
- " donation of the aforementioned amount. As a token of gratitude, upon"
- " successful payment, you may download your image at "
- "`http://%s:%u/download?ref=[]'";
- char *desc;
- json_t *json;
- char *json_str;
- uint64_t nounce;
- uint64_t product;
- uint64_t contract_id_nbo;
- long long contract_id;
- unsigned int ret;
-
- resp = NULL;
- contract = NULL;
- desc = NULL;
- ret = 400;
- product = UINT64_MAX;
- MHD_get_connection_values (connection, MHD_GET_ARGUMENT_KIND,
- &get_contract_values_iter, &product);
- if (UINT64_MAX == product)
- goto EXITIF_exit;
-
- expiry = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
- GNUNET_TIME_UNIT_DAYS);
- ROUND_TO_SECS (expiry, abs_value_us);
- amount.value = 1;
- amount.fraction = 0;
- strcpy (amount.currency, "EUR");
- nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
-
- /* Prepare contract */
- (void) GNUNET_asprintf (&desc,
- template,
- hostname,
- port);
- contract_id = MERCHANT_DB_contract_create (db_conn,
- expiry,
- &amount,
- desc,
- nounce,
- product);
- EXITIF (-1 == contract_id);
- contract_id_nbo = GNUNET_htonll ((uint64_t) contract_id);
- contract = GNUNET_malloc (sizeof (struct Contract) + strlen (desc) + 1);
- contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
- contract->purpose.size = htonl (sizeof (struct Contract)
- - offsetof (struct Contract, purpose)
- + strlen (desc) + 1);
- GNUNET_STRINGS_data_to_string (&contract_id_nbo, sizeof (contract_id_nbo),
- contract->m, sizeof (contract->m));
- contract->t = GNUNET_TIME_absolute_hton (expiry);
- (void) strcpy (contract->a, desc);
- contract->h_wire = hash_wireformat (nounce);
- contract->amount = TALER_amount_hton (amount);
- GNUNET_CRYPTO_eddsa_sign (privkey, &contract->purpose, &contract->sig);
- json = build_json_contract (contract);
- json_str = json_dumps (json, JSON_INDENT(2));
- json_decref (json);
- resp = MHD_create_response_from_buffer (strlen (json_str), json_str,
- MHD_RESPMEM_MUST_FREE);
- ret = 200;
-
- EXITIF_exit:
- GNUNET_free_non_null (desc);
- if (NULL != resp)
- *_resp = resp;
- if (NULL != contract)
- {
- GNUNET_free (contract);
- }
- return ret;
-}
-
-static struct Download *
-find_product (unsigned int id)
-{
- struct Download *dwn;
-
- for (dwn = dwn_head; NULL != dwn; dwn = dwn->next)
- {
- if (dwn->id == id)
- return dwn;
- }
- return NULL;
-}
-
-
-static int
-get_download_ref (void *cls,
- enum MHD_ValueKind kind,
- const char *key, const char *value)
-{
- char **coin_pub_enc = cls;
-
- if (0 == strncasecmp (key, "ref", sizeof ("ref")))
- {
- *coin_pub_enc = GNUNET_strdup (value);
- return MHD_NO;
- }
- return MHD_YES;
-}
-
-static unsigned int
-handle_download (struct MHD_Connection *conn,
- struct MHD_Response **_resp)
-{
- char *coin_pub_enc;
- struct Download *item;
- struct GNUNET_DISK_FileHandle *fh;
- struct GNUNET_CRYPTO_EddsaPublicKey coin_pub;
- long long product_id;
- off_t size;
- int ret;
-
- coin_pub_enc = NULL;
- ret = MHD_HTTP_NOT_FOUND;
- MHD_get_connection_values (conn, MHD_GET_ARGUMENT_KIND,
- &get_download_ref, &coin_pub_enc);
- LOG_WARNING ("Trying to start downloading with coin: %s\n", coin_pub_enc);
- EXITIF (NULL == coin_pub_enc);
- EXITIF (GNUNET_SYSERR ==
- GNUNET_CRYPTO_eddsa_public_key_from_string (coin_pub_enc,
- strlen (coin_pub_enc),
- &coin_pub));
- product_id = MERCHANT_DB_get_checkout_product (db_conn,
- &coin_pub);
- EXITIF (-1 == product_id);
- EXITIF (NULL == (item = find_product ((unsigned int) product_id)));
- if (NULL != item->resp)
- {
- *_resp = item->resp;
- ret = MHD_HTTP_OK;
- goto EXITIF_exit;
- }
- fh = GNUNET_DISK_file_open (item->filename,
- GNUNET_DISK_OPEN_READ,
- GNUNET_DISK_PERM_USER_READ);
- GNUNET_assert (NULL != fh);
- GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_handle_size (fh,
- &size));
- item->resp = MHD_create_response_from_fd (size, fh->fd);
- GNUNET_assert (MHD_NO != MHD_add_response_header (item->resp,
- "Content-Type",
- "image/jpeg"));
- GNUNET_free (fh);
-
- EXITIF_exit:
- GNUNET_free_non_null (coin_pub_enc);
- return ret;
-}
-
-struct CheckoutCtx
-{
- /* FIXME: Hook into a DLL for cleaner shutdown */
- struct MHD_Connection *conn;
- struct TALER_MINT_DepositHandle *dh;
- struct Download *product;
- char *coin_pub_enc;
- uint64_t transaction_id;
- struct GNUNET_CRYPTO_EddsaPublicKey coin_pub;
- struct GNUNET_CRYPTO_EddsaSignature coin_sig;
- struct TALER_Amount amount;
- struct GNUNET_SCHEDULER_Task *timeout_task;
-
-};
-
-
-/**
- * Callbacks of this type are used to serve the result of submitting a deposit
- * permission object to a mint
- *
- * @param cls closure
- * @param status 1 for successful deposit, 2 for retry, 0 for failure
- * @param obj the received JSON object; can be NULL if it cannot be constructed
- * from the reply
- * @param emsg in case of unsuccessful deposit, this contains a human readable
- * explanation.
- */
-static void
-checkout_status (void *cls, int status, json_t *obj, char *emsg)
-{
- struct CheckoutCtx *ctx = cls;
- const char *tmplt_download_page =
- "<!DOCTYPE HTML><html>"
- "<body>You are being redirected to the product download page<br>"
- "If your browser is unable to redirect, you may click "
- "<a href=\"%s\">here</a> to download.</body>"
- "</html>";
- char *download_page;
- char *location;
- struct MHD_Response *resp;
- int size;
-
- LOG_DEBUG ("Processing checkout request reply\n");
- GNUNET_SCHEDULER_cancel (ctx->timeout_task);
- ctx->timeout_task = NULL;
- download_page = NULL;
- location = NULL;
- switch (status)
- {
- case 1:
- {
- struct GNUNET_CRYPTO_EddsaPublicKey coin_pub;
-
- GNUNET_assert (GNUNET_SYSERR !=
- GNUNET_CRYPTO_eddsa_public_key_from_string
- (ctx->coin_pub_enc,
- strlen (ctx->coin_pub_enc),
- &coin_pub));
- /* FIXME: Put the contract into the checkout DB. */
- }
- /* redirect with HTTP FOUND 302 to the product download page */
- GNUNET_assert (NULL != obj);
- GNUNET_assert (0 < (size = GNUNET_asprintf (&location,
- "/download?ref=%s",
- ctx->coin_pub_enc)));
- GNUNET_assert (0 < (size = GNUNET_asprintf (&download_page,
- tmplt_download_page,
- location)));
- resp = MHD_create_response_from_buffer (size,
- download_page,
- MHD_RESPMEM_MUST_FREE);
- /* IMP: do not free `download_page' */
- GNUNET_assert (NULL != resp);
- GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
- "Location",
- location));
- GNUNET_assert (MHD_YES == MHD_queue_response (ctx->conn,
- MHD_HTTP_FOUND,
- resp));
- MHD_destroy_response (resp);
- GNUNET_free (location);
- location = NULL;
- resp = NULL;
-#if 0
- struct Download *product;
- struct GNUNET_DISK_FileHandle *fh;
- GNUNET_assert (NULL != (product = ctx->product));
- if (NULL != product->resp)
- {
- MHD_queue_response (ctx->conn, MHD_HTTP_OK, product->resp);
- break;
- }
- fh = GNUNET_DISK_file_open (product->filename,
- GNUNET_DISK_OPEN_READ,
- GNUNET_DISK_PERM_USER_READ);
- GNUNET_assert (NULL != fh);
- GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_handle_size (fh,
- &size));
- product->resp = MHD_create_response_from_fd (size, fh->fd);
- GNUNET_assert (MHD_NO != MHD_add_response_header (product->resp,
- "Content-Type",
- "image/jpeg"));
- GNUNET_free (fh);
- MHD_queue_response (ctx->conn, MHD_HTTP_OK, product->resp);
-#endif
-
- break;
- case 2:
- send_response_json (ctx->conn,
- json_pack ("{s:s}", "status", "pending"),
- 200); /* FIXME: Send Image data */
- break;
- case 0:
- send_response_json (ctx->conn,
- json_pack ("{s:s s:s}",
- "status", "failed",
- "error", (NULL != emsg) ? emsg : "unknown"),
- 400); /* FIXME */
- break;
- default:
- GNUNET_assert (0); /* should never reach */
- }
- GNUNET_free (ctx->coin_pub_enc);
- GNUNET_free (ctx);
- if (GNUNET_SYSERR == poll_mhd ())
- {
- GNUNET_break (0);
- GNUNET_SCHEDULER_shutdown ();
- }
-}
-
-static void
-checkout_status_timedout (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- struct CheckoutCtx *ctx = cls;
-
- LOG_DEBUG ("Checkout request timed out\n");
- ctx->timeout_task = NULL;
- TALER_MINT_deposit_submit_cancel (ctx->dh);
- ctx->dh = NULL;
- send_response_json (ctx->conn,
- json_pack ("{s:s}", "error", "timeout"),
- 400); /* FIXME */
- GNUNET_free (ctx->coin_pub_enc);
- GNUNET_free (ctx);
- EXITIF (GNUNET_SYSERR == poll_mhd ());
- return;
-
- EXITIF_exit:
- GNUNET_SCHEDULER_shutdown ();
-}
-
-static int
-handle_checkout (struct MHD_Connection *conn,
- json_t *checkout_json)
-{
- struct CheckoutCtx *ctx;
- const char *pkey_enc;
- const char *tid_enc;
- const char *emsg;
- const char *coin_pub_enc;
- const char *coin_sig_enc;
- struct Mint *mint;
- struct Download *product;
- struct GNUNET_CRYPTO_EddsaPublicKey pkey;
- struct GNUNET_CRYPTO_EddsaPublicKey coin_pub;
- struct GNUNET_CRYPTO_EddsaSignature coin_sig;
- uint64_t tid;
- uint64_t product_id;
- json_error_t jerror;
- unsigned int status;
-
- coin_pub_enc = NULL;
- emsg = "Public key of Mint is missing in the request";
- status = MHD_HTTP_BAD_REQUEST;
- if (-1 == json_unpack_ex (checkout_json,
- &jerror,
- 0,
- "{s:s s:s s:s s:s}",
- "mint_pub", &pkey_enc,
- "transaction_id", &tid_enc,
- "coin_pub", &coin_pub_enc,
- "coin_sig", &coin_sig_enc))
- {
- WARN_JSON (jerror);
- goto EXITIF_exit;
- }
-
- EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data
- (tid_enc, strlen (tid_enc), &tid, sizeof (tid)));
- tid = GNUNET_ntohll (tid);
-
- emsg = "Public key of the coin is missing/malformed in the request";
- EXITIF (NULL == coin_pub_enc);
- EXITIF (GNUNET_SYSERR ==
- GNUNET_CRYPTO_eddsa_public_key_from_string (coin_pub_enc,
- strlen (coin_pub_enc),
- &coin_pub));
-
- emsg = "Signature of the coin is missing/malformed in the request";
- EXITIF (NULL == coin_sig_enc);
- EXITIF (GNUNET_SYSERR ==
- GNUNET_STRINGS_string_to_data (coin_sig_enc, strlen (coin_sig_enc),
- &coin_sig, sizeof (coin_sig)));
-
- emsg = "Contract not found";
- status = MHD_HTTP_NOT_FOUND;
- LOG_DEBUG ("Looking for product associated with transaction %u\n", tid);
- EXITIF (-1 == (product_id = MERCHANT_DB_get_contract_product (db_conn, tid)));
-
- emsg = "Could not find the downloadable product. Sorry :(";
- EXITIF (NULL == (product = find_product (product_id)));
-
- emsg = "Invalid public key given for a mint";
- EXITIF (52 != strlen (pkey_enc));
- EXITIF (GNUNET_SYSERR == GNUNET_STRINGS_string_to_data (pkey_enc, 52,
- &pkey, sizeof (pkey)));
-
- emsg = "The provided mint is not trusted by us";
- status = MHD_HTTP_FORBIDDEN;
- EXITIF (NULL == (mint =
- GNUNET_CONTAINER_multipeermap_get (mints_map,
- (const struct
- GNUNET_PeerIdentity *)
- &pkey)));
-
- LOG_DEBUG ("Creating a new checkout request\n");
- ctx = GNUNET_new (struct CheckoutCtx);
- ctx->product = product;
- ctx->conn = conn;
- ctx->coin_pub_enc = GNUNET_strdup (coin_pub_enc);
- ctx->transaction_id = tid;
- ctx->coin_pub = coin_pub;
- ctx->coin_sig = coin_sig;
- /* FIXME: parse amount */
- /* ctx->amount = ?? */
- ctx->dh = TALER_MINT_deposit_submit_json (mint->conn,
- checkout_status,
- ctx,
- checkout_json);
- ctx->timeout_task = GNUNET_SCHEDULER_add_delayed
- (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3),
- &checkout_status_timedout, ctx);
- return MHD_YES;
-
- EXITIF_exit:
- json_decref (checkout_json);
- return send_response_json (conn,
- json_pack ("{s:s s:s}",
- "status", "failed",
- "error", emsg),
- status);
-}
-
-/**
- * A client has requested the given url using the given method
- * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
- * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
- * must call MHD callbacks to provide content to give back to the
- * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
- * #MHD_HTTP_NOT_FOUND, etc.).
- *
- * @param cls argument given together with the function
- * pointer when the handler was registered with MHD
- * @param url the requested url
- * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
- * #MHD_HTTP_METHOD_PUT, etc.)
- * @param version the HTTP version string (i.e.
- * #MHD_HTTP_VERSION_1_1)
- * @param upload_data the data being uploaded (excluding HEADERS,
- * for a POST that fits into memory and that is encoded
- * with a supported encoding, the POST data will NOT be
- * given in upload_data and is instead available as
- * part of #MHD_get_connection_values; very large POST
- * data *will* be made available incrementally in
- * @a upload_data)
- * @param upload_data_size set initially to the size of the
- * @a upload_data provided; the method must update this
- * value to the number of bytes NOT processed;
- * @param con_cls pointer that the callback can set to some
- * address and that will be preserved by MHD for future
- * calls for this request; since the access handler may
- * be called many times (i.e., for a PUT/POST operation
- * with plenty of upload data) this allows the application
- * to easily associate some request-specific state.
- * If necessary, this state can be cleaned up in the
- * global #MHD_RequestCompletedCallback (which
- * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
- * Initially, `*con_cls` will be NULL.
- * @return #MHD_YES if the connection was handled successfully,
- * #MHD_NO if the socket must be closed due to a serios
- * error while handling the request
- */
-static int
-url_handler (void *cls,
- struct MHD_Connection *connection,
- const char *url,
- const char *method,
- const char *version,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
-#define URL_PRODUCTS "/products"
-#define URL_CONTRACT "/contract"
-#define URL_CHECKOUT "/checkout"
-#define URL_HTTPTEST "/httptest"
-#define URL_DOWNLOAD "/download"
-#define STR_404_NOTFOUND "The requested resource is not found"
- struct MHD_Response *resp;
- int no_destroy;
- unsigned int status;
-
- resp = NULL;
- status = 404;
- no_destroy = 0;
- LOG_DEBUG ("request for URL `%s'\n", url);
-
- if (0 == strncasecmp (url, URL_PRODUCTS, sizeof (URL_PRODUCTS)))
- {
- /* parse for /contract */
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- {
- resp = list_products_resp;
- no_destroy = 1;
- status = 200;
- }
- else
- GNUNET_break (0); /* FIXME: implement for post */
- }
-
- if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT)))
- {
- /* parse for /contract */
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- status = handle_get_contract (connection, &resp);
- else
- GNUNET_break (0); /* FIXME: implement for post */
- }
-
- if (0 == strncasecmp (url, URL_CHECKOUT, sizeof (URL_CHECKOUT)))
- {
- json_t *checkout_obj;
- int ret;
- /* parse for /checkout */
- ret = process_post_json (connection,
- con_cls,
- upload_data,
- upload_data_size,
- &checkout_obj);
- if (GNUNET_SYSERR == ret)
- return MHD_NO;
- if (GNUNET_NO == ret)
- return MHD_YES;
- /* Handle the response in the request handler */
- ret = handle_checkout (connection, checkout_obj);
- return ret;
- }
-
- if (0 == strncasecmp (url, URL_HTTPTEST, sizeof (URL_HTTPTEST)))
- {
- static char page[]="\
-<!DOCTYPE html> \
-<html><title>HTTP Test page</title><body><center><h3>HTTP Test page</h3> \
-</center></body></html>";
- resp = MHD_create_response_from_buffer (sizeof (page) - 1,
- page,
- MHD_RESPMEM_PERSISTENT);
- EXITIF (NULL == resp);
- }
-
- if ((0 == strncasecmp (url, URL_DOWNLOAD, sizeof (URL_DOWNLOAD)))
- && (0 == strcmp (MHD_HTTP_METHOD_GET, method)))
- {
- status = handle_download (connection, &resp);
- if (status != MHD_HTTP_OK)
- no_destroy = 1;
- }
- if (NULL != resp)
- {
- EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
- if (!no_destroy)
- MHD_destroy_response (resp);
- }
- else
- EXITIF (GNUNET_OK != failure_resp (connection, status));
- return MHD_YES;
-
- EXITIF_exit:
- result = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
- return MHD_NO;
-}
-
-
-/**
- * Callback for catching serious error conditions from MHD.
- *
- * @param cls user specified value
- * @param file where the error occured
- * @param line where the error occured
- * @param reason error detail, may be NULL
- */
-static void
-mhd_panic_cb (void *cls,
- const char *file,
- unsigned int line,
- const char *reason)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "MHD panicked at %s:%u: %s",
- file, line, reason);
- result = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
-}
-
-/**
- * Function called with a filename.
- *
- * @param cls closure
- * @param filename complete filename (absolute path)
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to stop iteration with no error,
- * #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-add_download_file (void *cls, const char *filename)
-{
- struct Download *dwn;
-
- dwn = GNUNET_new (struct Download);
- dwn->filename = GNUNET_strdup (filename);
- dwn->id = ndownloads++;
- GNUNET_CONTAINER_DLL_insert (dwn_head, dwn_tail, dwn);
- return GNUNET_OK;
-}
-
-
-/**
- * Function to build a MHD response object to list products
- *
- * @return GNUNET_OK upon success; GNUNET_SYSERR otherwise
- */
-static int
-build_list_product_response ()
-{
- const char *header = "\
-<!DOCTYPE html> \
-<html><title>Products List</title> \
-<body><center><ol>";
- char **partials;
- const char *footer = "</ol></center></body></html>";
- char *page;
- struct Download *dwn;
- size_t size;
- unsigned int cnt;
- int psize;
- unsigned int header_size;
- unsigned int footer_size;
- unsigned int *partial_sizes;
- int ret;
-
- ret = GNUNET_SYSERR;
- GNUNET_assert (NULL == list_products_resp);
- header_size = strlen (header);
- footer_size = strlen (footer);
- size = header_size;
- size += footer_size;
- partials = GNUNET_malloc (sizeof (char *) * ndownloads);
- partial_sizes = GNUNET_malloc (sizeof (unsigned int) * ndownloads);
- EXITIF (0 == ndownloads);
- for (cnt = 0, dwn = dwn_head; cnt < ndownloads; cnt++, dwn=dwn->next)
- {
- EXITIF (NULL == dwn);
- psize = GNUNET_asprintf (&partials[cnt],
- "<li><a href=\"/contract?product=%u\">%s</a></li>",
- cnt,
- GNUNET_STRINGS_get_short_name (dwn->filename));
- EXITIF (psize < 0);
- size += psize;
- partial_sizes [cnt] = psize;
- }
- page = GNUNET_malloc (size);
- size = 0;
- (void) memcpy (page, header, header_size);
- size += header_size;
- for (cnt = 0; cnt < ndownloads; cnt++)
- {
- (void) memcpy (page + size, partials[cnt], partial_sizes[cnt]);
- size += partial_sizes[cnt];
- }
- (void) memcpy (page + size, footer, footer_size);
- size += footer_size;
- list_products_resp = MHD_create_response_from_buffer (size, page, MHD_RESPMEM_MUST_FREE);
- ret = GNUNET_OK;
-
- EXITIF_exit:
- for (cnt = 0; cnt < ndownloads; cnt++)
- GNUNET_free_non_null (partials[cnt]);
- GNUNET_free_non_null (partials);
- GNUNET_free_non_null (partial_sizes);
- return ret;
-}
-
-/**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param config configuration
- */
-static void
-run (void *cls, char *const *args, const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *config)
-{
- char *keyfile;
- struct MERCHANT_MintInfo *mint_infos;
- unsigned int nmints;
- unsigned int cnt;
-
- result = GNUNET_SYSERR;
- keyfile = NULL;
- mint_infos = NULL;
- shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &do_shutdown, NULL);
- if (NULL == data_dir)
- {
- LOG_ERROR ("Data directory for download files is missing. It can be given with the `-d' option\n");
- goto EXITIF_exit;
- }
- EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config,
- &mint_infos)));
- EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config)));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config,
- "merchant",
- "KEYFILE",
- &keyfile));
- EXITIF (NULL == (privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
- EXITIF (0 == GNUNET_DISK_directory_scan (data_dir,
- &add_download_file,
- NULL));
- EXITIF (GNUNET_SYSERR == build_list_product_response ());
- EXITIF (NULL == (db_conn = MERCHANT_DB_connect (config)));
- EXITIF (GNUNET_OK != MERCHANT_DB_initialise (db_conn, dry));
- EXITIF (GNUNET_SYSERR ==
- GNUNET_CONFIGURATION_get_value_number (config,
- "merchant",
- "port",
- &port));
- EXITIF (GNUNET_SYSERR ==
- GNUNET_CONFIGURATION_get_value_string (config,
- "merchant",
- "hostname",
- &hostname));
- EXITIF (NULL == (mctx = TALER_MINT_init ()));
- EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES)));
- for (cnt = 0; cnt < nmints; cnt++)
- {
- struct Mint *mint;
-
- mint = GNUNET_new (struct Mint);
- mint->pubkey = mint_infos[cnt].pubkey;
- mint->conn = TALER_MINT_connect (mctx,
- mint_infos[cnt].hostname,
- mint_infos[cnt].port,
- &mint->pubkey);
- EXITIF (NULL == mint->conn);
- EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put
- (mints_map,
- (struct GNUNET_PeerIdentity *) &mint->pubkey,
- mint,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
- }
- MHD_set_panic_func (&mhd_panic_cb, NULL);
- mhd = MHD_start_daemon (MHD_USE_DEBUG, //| MHD_USE_TCP_FASTOPEN,
- (unsigned short) port,
- NULL, NULL,
- &url_handler, NULL,
- //MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE,
- //(unsigned int) 16,
- MHD_OPTION_END);
- EXITIF (NULL == mhd);
- EXITIF (GNUNET_SYSERR == poll_mhd ());
- GNUNET_CRYPTO_hash (wire, sizeof (*wire), &h_wire);
- result = GNUNET_OK;
-
- EXITIF_exit:
- if (NULL != mint_infos)
- {
- for (cnt = 0; cnt < nmints; cnt++)
- GNUNET_free (mint_infos[cnt].hostname);
- GNUNET_free (mint_infos);
- }
- GNUNET_free_non_null (keyfile);
- if (GNUNET_OK != result)
- GNUNET_SCHEDULER_shutdown ();
-}
-
-
-/**
- * The main function of the serve tool
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc, char *const *argv)
-{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- {'t', "temp", NULL,
- gettext_noop ("Use temporary database tables"), GNUNET_NO,
- &GNUNET_GETOPT_set_one, &dry},
- {'d', "dir", "DIRECTORY",
- gettext_noop ("Directory of the data files to serve"), GNUNET_YES,
- &GNUNET_GETOPT_set_string, &data_dir},
- GNUNET_GETOPT_OPTION_END
- };
-
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run (argc, argv,
- "taler-merchant-serve",
- "Serve merchant's HTTP interface",
- options, &run, NULL))
- return 3;
- return (GNUNET_OK == result) ? 0 : 1;
-}
diff --git a/src/merchant/test_merchant.c b/src/merchant/test_merchant.c
deleted file mode 100644
index bcec09ae..00000000
--- a/src/merchant/test_merchant.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/test_merchant.c
- * @brief File to test merchant-internal helper functions.
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include "merchant.h"
-
-/**
- * Array of parsed mints
- */
-struct MERCHANT_MintInfo *mints;
-
-/**
- * Number of mints in the above array
- */
-int n_mints;
-
-/**
- * Test result
- */
-static int result;
-
-static void
-do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- unsigned int cnt;
-
- for (cnt=0; cnt < n_mints; cnt++)
- GNUNET_free (mints[cnt].hostname);
- GNUNET_free_non_null (mints);
- mints = 0;
-}
-
-
-/**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param config configuration
- */
-static void
-run (void *cls, char *const *args, const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *config)
-{
-
- mints = NULL;
- n_mints = GNUNET_SYSERR;
- n_mints = TALER_MERCHANT_parse_mints (config, &mints);
- GNUNET_assert (GNUNET_SYSERR != n_mints);
- GNUNET_assert (NULL != mints);
- result = GNUNET_OK;
- GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
-}
-
-int
-main (int argc, char *const argv[])
-{
- char *argv2[] = {
- "test-merchant",
- "-c", "test_merchant.conf",
- NULL
- };
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_END
- };
-
- result = GNUNET_SYSERR;
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1,
- argv2, "test-merchant",
- "File to test merchant-internal helper functions.",
- options, &run, NULL))
- return 3;
- return (GNUNET_OK == result) ? 0 : 1;
-}
diff --git a/src/merchant/test_merchant.conf b/src/merchant/test_merchant.conf
deleted file mode 100644
index 7d32769a..00000000
--- a/src/merchant/test_merchant.conf
+++ /dev/null
@@ -1,36 +0,0 @@
-# Sample configuration file test-merchant testcase
-[merchant]
-PORT = 4251
-HOSTNAME = localhost
-# List of mints the merchant trusts delimited by a single space
-TRUSTED_MINTS = nayapaisa
-
-#nayapaisa nyadirahim
-
-# Our secret key file
-KEYFILE = test_merchant.ecc
-
-[mint-taler]
-HOSTNAME = localhost
-PORT = 4241
-PUBKEY = 8MF3D4V40K4J7654Q7NT3YHZVJ22SJAMBG07GEQ0QSXHWF4M9X6G
-
-[mint-nayapaisa]
-HOSTNAME = localhost
-PORT = 4241
-# The public key of this mint
-PUBKEY = 6ZE0HEY2M0FWP61M0470HYBF4K6RRD5DP54372PD2TN9N9VX2VJG
-
-[mint-nyadirahim]
-HOSTNAME = nyadirahim.org
-PORT = 4241
-# The public key of this mint
-PUBKEY = 7995WKK71KPKTBBMA5BHNBSZFGNRZPYNXDJMQ8EK86V9598H03TG
-
-[merchant-db]
-CONFIG = postgres:///taler
-
-[wire-sepa]
-IBAN = DE67830654080004822650
-NAME = GNUNET E.V
-BIC = GENODEF1SRL
diff --git a/src/merchant/test_merchant.ecc b/src/merchant/test_merchant.ecc
deleted file mode 100644
index ac8ee6e9..00000000
--- a/src/merchant/test_merchant.ecc
+++ /dev/null
@@ -1,2 +0,0 @@
-MY{.ӂce
-ف \ No newline at end of file
diff --git a/src/merchant/test_merchant_db.c b/src/merchant/test_merchant_db.c
deleted file mode 100644
index caaa1be4..00000000
--- a/src/merchant/test_merchant_db.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchant/test_merchant_db.c
- * @brief File to test merchant database helper functions.
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <taler/taler_util.h>
-#include "merchant_db.h"
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-/**
- * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
- */
-#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000);
-
-/**
- * The database handle
- */
-PGconn *conn;
-
-/**
- * Test result
- */
-static int result;
-
-static void
-do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- if (NULL != conn)
- MERCHANT_DB_disconnect (conn);
- conn = NULL;
-}
-
-
-/**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param config configuration
- */
-static void
-run (void *cls, char *const *args, const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *config)
-{
- struct GNUNET_TIME_Absolute expiry;
- struct TALER_Amount amount;
- char *desc = "A contract from GNUnet e.V to say a big Thank You for a donation of the aforementioned amount.";
- uint64_t nounce;
- uint64_t product;
- long long transaction_id;
-
- conn = MERCHANT_DB_connect (config);
- EXITIF (NULL == conn);
- GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
- EXITIF (GNUNET_OK != MERCHANT_DB_initialise (conn, GNUNET_YES));
- expiry = GNUNET_TIME_absolute_get ();
- expiry = GNUNET_TIME_absolute_add (expiry, GNUNET_TIME_UNIT_DAYS);
- ROUND_TO_SECS (expiry, abs_value_us);
- amount.value = 1;
- amount.fraction = 0;
- nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
- product = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
- product &= (UINT64_MAX >> 1);
- EXITIF (-1 == (transaction_id = MERCHANT_DB_contract_create (conn,
- expiry,
- &amount,
- desc,
- nounce,
- product)));
- {
- struct GNUNET_CRYPTO_EddsaPublicKey coin_pub;
- struct GNUNET_CRYPTO_EddsaSignature coin_sig;
- long long paid_product;
-
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
- &coin_pub, sizeof (coin_pub));
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
- &coin_sig, sizeof (coin_sig));
- EXITIF (GNUNET_SYSERR == MERCHANT_DB_checkout_create (conn,
- &coin_pub,
- transaction_id,
- &amount,
- &coin_sig));
- EXITIF (-1 == (paid_product = MERCHANT_DB_get_checkout_product (conn,
- &coin_pub)));
- EXITIF (paid_product < 0);
- EXITIF (((uint64_t) paid_product) != product);
- /* We should get -1 for product if a coin is not paid to us */
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
- &coin_pub, sizeof (coin_pub));
- EXITIF (-1 != (product = MERCHANT_DB_get_checkout_product (conn,
- &coin_pub)));
- }
- result = GNUNET_OK;
-
- EXITIF_exit:
- return;
-}
-
-int
-main (int argc, char *const argv[])
-{
- char *argv2[] = {
- "test-merchant-db",
- "-c", "test_merchant.conf",
- NULL
- };
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_END
- };
-
- result = GNUNET_SYSERR;
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1,
- argv2, "test-merchant-db",
- "File to test merchant database helper functions.",
- options, &run, NULL))
- return 3;
- return (GNUNET_OK == result) ? 0 : 1;
-}
diff --git a/src/tests/test_contract.c b/src/tests/test_contract.c
index 147ea4f3..68572a38 100644
--- a/src/tests/test_contract.c
+++ b/src/tests/test_contract.c
@@ -54,13 +54,13 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
if (NULL != db_conn)
{
- MERCHANT_DB_disconnect (db_conn);
+ TALER_MERCHANTDB_disconnect (db_conn);
db_conn = NULL;
}
}
extern uint32_t
-MERCHANT_DB_get_contract_values (PGconn *conn,
+TALER_MERCHANTDB_contract_get_values (PGconn *conn,
const struct GNUNET_HashCode *h_contract,
uint64_t *nounce,
struct GNUNET_TIME_Absolute *edate);
@@ -116,8 +116,8 @@ run (void *cls, char *const *args, const char *cfgfile,
wire = NULL;
- db_conn = MERCHANT_DB_connect (config);
- if (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_NO))
+ db_conn = TALER_MERCHANTDB_connect (config);
+ if (GNUNET_OK != TALER_MERCHANTDB_initialize (db_conn, GNUNET_NO))
{
printf ("no db init'd\n");
result = GNUNET_SYSERR;
@@ -293,7 +293,7 @@ run (void *cls, char *const *args, const char *cfgfile,
printf ("contract string : %s\n", aa);
GNUNET_CRYPTO_hash (aa, strlen (aa) + 1, &h_contract_str);
- if (GNUNET_SYSERR == MERCHANT_DB_get_contract_values (db_conn, &h_contract_str, &nounce, &edate))
+ if (GNUNET_SYSERR == TALER_MERCHANTDB_contract_get_values (db_conn, &h_contract_str, &nounce, &edate))
printf ("no hash found\n");
else
{