aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2023-05-12 13:40:22 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2023-05-12 13:40:22 +0200
commit4833234df63d7da774299b336adb925e700bd4b4 (patch)
treeabb478b860c2d3f292ded9364300ce73b1d60d11
parent9130cda9e775131d3ced613b7f238a4c9e43ad5a (diff)
parentff1a28319fe31741958a0b1cfa761fd44878db45 (diff)
downloadexchange-4833234df63d7da774299b336adb925e700bd4b4.tar.xz
Merge branch 'master' into age-withdraw
m---------contrib/gana0
-rw-r--r--contrib/gnunet.tag8
-rw-r--r--doc/doxygen/taler.doxy56
-rw-r--r--doc/flows/Makefile3
-rw-r--r--doc/flows/fees-coins.tex39
-rw-r--r--doc/flows/fees-wire.tex30
-rw-r--r--doc/flows/int-deposit.tex52
-rw-r--r--doc/flows/int-pay.tex58
-rw-r--r--doc/flows/int-pull.tex55
-rw-r--r--doc/flows/int-push.tex47
-rw-r--r--doc/flows/int-refund.tex39
-rw-r--r--doc/flows/int-shutdown.tex48
-rw-r--r--doc/flows/int-withdraw.tex49
-rw-r--r--doc/flows/kyc-balance.tex57
-rw-r--r--doc/flows/kyc-deposit.tex71
-rw-r--r--doc/flows/kyc-pull.tex78
-rw-r--r--doc/flows/kyc-push.tex79
-rw-r--r--doc/flows/kyc-withdraw.tex45
-rw-r--r--doc/flows/main.tex159
-rw-r--r--doc/flows/proc-aml.tex47
-rw-r--r--doc/flows/proc-domestic.tex66
-rw-r--r--doc/flows/proc-kyc.tex88
-rwxr-xr-xsrc/auditor/batch.sh2
-rwxr-xr-xsrc/auditor/generate-auditor-basedb.sh2
-rwxr-xr-xsrc/auditor/generate-revoke-basedb.sh2
-rw-r--r--src/auditor/taler-auditor-sync.c3
-rw-r--r--src/auditor/taler-helper-auditor-coins.c6
-rw-r--r--src/auditor/taler-helper-auditor-wire.c32
-rw-r--r--src/bank-lib/bank_api_credit.c4
-rw-r--r--src/bank-lib/bank_api_debit.c4
-rw-r--r--src/bank-lib/bank_api_transfer.c12
-rw-r--r--src/bank-lib/fakebank.c6
-rw-r--r--src/bank-lib/taler-exchange-wire-gateway-client.c8
-rw-r--r--src/exchange-tools/taler-auditor-offline.c23
-rw-r--r--src/exchange-tools/taler-exchange-offline.c244
-rw-r--r--src/exchange/taler-exchange-closer.c8
-rw-r--r--src/exchange/taler-exchange-httpd.c37
-rw-r--r--src/exchange/taler-exchange-httpd_age-withdraw_reveal.c168
-rw-r--r--src/exchange/taler-exchange-httpd_config.h2
-rw-r--r--src/exchange/taler-exchange-httpd_deposits_get.c323
-rw-r--r--src/exchange/taler-exchange-httpd_deposits_get.h7
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c3
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-check.c30
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-proof.c27
-rw-r--r--src/exchange/taler-exchange-httpd_management_wire_disable.c5
-rw-r--r--src/exchange/taler-exchange-httpd_management_wire_enable.c46
-rw-r--r--src/exchange/taler-exchange-httpd_metrics.h3
-rw-r--r--src/exchange/taler-exchange-httpd_purses_delete.c26
-rw-r--r--src/exchange/taler-exchange-httpd_purses_get.c32
-rw-r--r--src/exchange/taler-exchange-httpd_refreshes_reveal.c10
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_get.c44
-rw-r--r--src/exchange/taler-exchange-httpd_wire.c17
-rw-r--r--src/exchange/taler-exchange-transfer.c6
-rw-r--r--src/exchange/taler-exchange-wirewatch.c4
-rw-r--r--src/exchangedb/.gitignore3
-rw-r--r--src/exchangedb/0003-age_withdraw_commitments.sql (renamed from src/exchangedb/0003-withdraw_age_commitments.sql)66
-rw-r--r--src/exchangedb/0003-age_withdraw_reveals.sql (renamed from src/exchangedb/0003-withdraw_age_reveals.sql)33
-rw-r--r--src/exchangedb/0004-wire_accounts.sql26
-rw-r--r--src/exchangedb/Makefile.am13
-rw-r--r--src/exchangedb/bench_db.c12
-rw-r--r--src/exchangedb/drop.sql1
-rw-r--r--src/exchangedb/exchange-0003.sql.in2
-rw-r--r--src/exchangedb/exchange-0004.sql.in24
-rw-r--r--src/exchangedb/pg_aggregate.c69
-rw-r--r--src/exchangedb/pg_get_age_withdraw_info.c2
-rw-r--r--src/exchangedb/pg_get_wire_accounts.c32
-rw-r--r--src/exchangedb/pg_insert_age_withdraw_reveal.c4
-rw-r--r--src/exchangedb/pg_insert_records_by_table.c60
-rw-r--r--src/exchangedb/pg_insert_wire.c15
-rw-r--r--src/exchangedb/pg_insert_wire.h6
-rw-r--r--src/exchangedb/pg_lookup_records_by_table.c64
-rw-r--r--src/exchangedb/pg_lookup_serial_by_table.c20
-rw-r--r--src/exchangedb/pg_update_wire.c20
-rw-r--r--src/exchangedb/pg_update_wire.h8
-rw-r--r--src/extensions/age_restriction/age_restriction.c6
-rw-r--r--src/include/taler_bank_service.h6
-rw-r--r--src/include/taler_crypto_lib.h24
-rw-r--r--src/include/taler_exchange_service.h695
-rw-r--r--src/include/taler_exchangedb_plugin.h85
-rw-r--r--src/include/taler_extensions.h4
-rw-r--r--src/include/taler_json_lib.h27
-rw-r--r--src/include/taler_mhd_lib.h225
-rw-r--r--src/include/taler_templating_lib.h22
-rw-r--r--src/include/taler_testing_lib.h38
-rw-r--r--src/include/taler_util.h64
-rw-r--r--src/json/Makefile.am16
-rw-r--r--src/json/json.c169
-rw-r--r--src/json/json_wire.c74
-rw-r--r--src/json/test_json_wire.c80
-rw-r--r--src/kyclogic/Makefile.am6
-rw-r--r--src/kyclogic/kyclogic-persona.conf4
-rw-r--r--src/kyclogic/kyclogic_api.c9
-rw-r--r--src/kyclogic/plugin_kyclogic_oauth2.c120
-rw-r--r--src/kyclogic/plugin_kyclogic_persona.c374
-rwxr-xr-xsrc/kyclogic/taler-exchange-kyc-persona-converter.sh54
-rw-r--r--src/kyclogic/taler-exchange-kyc-tester.c6
-rw-r--r--src/lib/exchange_api_batch_deposit.c12
-rw-r--r--src/lib/exchange_api_batch_withdraw.c37
-rw-r--r--src/lib/exchange_api_batch_withdraw2.c68
-rw-r--r--src/lib/exchange_api_common.c192
-rw-r--r--src/lib/exchange_api_contracts_get.c8
-rw-r--r--src/lib/exchange_api_csr_melt.c4
-rw-r--r--src/lib/exchange_api_csr_withdraw.c2
-rw-r--r--src/lib/exchange_api_deposit.c8
-rw-r--r--src/lib/exchange_api_deposits_get.c48
-rw-r--r--src/lib/exchange_api_handle.c104
-rw-r--r--src/lib/exchange_api_kyc_check.c20
-rw-r--r--src/lib/exchange_api_link.c4
-rw-r--r--src/lib/exchange_api_lookup_aml_decision.c14
-rw-r--r--src/lib/exchange_api_lookup_aml_decisions.c6
-rw-r--r--src/lib/exchange_api_management_drain_profits.c28
-rw-r--r--src/lib/exchange_api_management_get_keys.c78
-rw-r--r--src/lib/exchange_api_management_post_extensions.c22
-rw-r--r--src/lib/exchange_api_management_post_keys.c26
-rw-r--r--src/lib/exchange_api_management_revoke_denomination_key.c22
-rw-r--r--src/lib/exchange_api_management_revoke_signing_key.c22
-rw-r--r--src/lib/exchange_api_management_update_aml_officer.c26
-rw-r--r--src/lib/exchange_api_management_wire_disable.c30
-rw-r--r--src/lib/exchange_api_management_wire_enable.c38
-rw-r--r--src/lib/exchange_api_melt.c10
-rw-r--r--src/lib/exchange_api_purse_deposit.c16
-rw-r--r--src/lib/exchange_api_purse_merge.c14
-rw-r--r--src/lib/exchange_api_purses_get.c14
-rw-r--r--src/lib/exchange_api_recoup.c69
-rw-r--r--src/lib/exchange_api_recoup_refresh.c67
-rw-r--r--src/lib/exchange_api_refreshes_reveal.c4
-rw-r--r--src/lib/exchange_api_refund.c75
-rw-r--r--src/lib/exchange_api_transfers_get.c107
-rw-r--r--src/lib/exchange_api_wire.c282
-rw-r--r--src/lib/exchange_api_withdraw.c28
-rw-r--r--src/lib/exchange_api_withdraw2.c69
-rw-r--r--src/mhd/mhd_parsing.c167
-rw-r--r--src/pq/pq_query_helper.c84
-rw-r--r--src/pq/pq_result_helper.c66
-rw-r--r--src/templating/.gitignore1
-rw-r--r--src/templating/AUTHORS9
-rw-r--r--src/templating/LICENSE-2.0.txt202
-rw-r--r--src/templating/LICENSE.txt14
-rw-r--r--src/templating/Makefile.am11
-rw-r--r--src/templating/README.md306
-rw-r--r--src/templating/mustach-cjson.c239
-rw-r--r--src/templating/mustach-cjson.h96
-rw-r--r--src/templating/mustach-jansson.c580
-rw-r--r--src/templating/mustach-jansson.h72
-rw-r--r--src/templating/mustach-json-c.c267
-rw-r--r--src/templating/mustach-json-c.h160
-rw-r--r--src/templating/mustach-tool.c176
-rw-r--r--src/templating/mustach-wrap.c456
-rw-r--r--src/templating/mustach-wrap.h234
-rw-r--r--src/templating/mustach.c256
-rw-r--r--src/templating/mustach.h172
-rw-r--r--src/templating/templating_api.c60
-rw-r--r--src/testing/Makefile.am2
-rw-r--r--src/testing/test_bank_api.c9
-rw-r--r--src/testing/test_bank_api_twisted.c6
-rw-r--r--src/testing/test_exchange_api-twisted.conf (renamed from src/testing/test_exchange_api_twisted.conf)4
-rw-r--r--src/testing/test_exchange_api_twisted.c166
-rw-r--r--src/testing/testing_api_cmd_bank_history_credit.c4
-rw-r--r--src/testing/testing_api_cmd_bank_history_debit.c4
-rw-r--r--src/testing/testing_api_cmd_batch.c6
-rw-r--r--src/testing/testing_api_cmd_batch_deposit.c10
-rw-r--r--src/testing/testing_api_cmd_batch_withdraw.c2
-rw-r--r--src/testing/testing_api_cmd_check_aml_decision.c4
-rw-r--r--src/testing/testing_api_cmd_contract_get.c10
-rw-r--r--src/testing/testing_api_cmd_deposit.c6
-rw-r--r--src/testing/testing_api_cmd_deposits_get.c5
-rw-r--r--src/testing/testing_api_cmd_exec_wget.c158
-rw-r--r--src/testing/testing_api_cmd_nexus_fetch_transactions.c187
-rw-r--r--src/testing/testing_api_cmd_oauth.c1
-rw-r--r--src/testing/testing_api_cmd_purse_create_deposit.c4
-rw-r--r--src/testing/testing_api_cmd_purse_deposit.c10
-rw-r--r--src/testing/testing_api_cmd_purse_get.c4
-rw-r--r--src/testing/testing_api_cmd_recoup.c17
-rw-r--r--src/testing/testing_api_cmd_recoup_refresh.c9
-rw-r--r--src/testing/testing_api_cmd_refresh.c40
-rw-r--r--src/testing/testing_api_cmd_refund.c13
-rw-r--r--src/testing/testing_api_cmd_revoke_denom_key.c5
-rw-r--r--src/testing/testing_api_cmd_revoke_sign_key.c7
-rw-r--r--src/testing/testing_api_cmd_set_officer.c6
-rw-r--r--src/testing/testing_api_cmd_transfer_get.c223
-rw-r--r--src/testing/testing_api_cmd_wire.c75
-rw-r--r--src/testing/testing_api_cmd_wire_add.c22
-rw-r--r--src/testing/testing_api_cmd_wire_del.c7
-rw-r--r--src/testing/testing_api_cmd_withdraw.c8
-rw-r--r--src/testing/testing_api_helpers_bank.c23
-rw-r--r--src/testing/testing_api_loop.c63
-rw-r--r--src/util/.gitignore1
-rw-r--r--src/util/Makefile.am13
-rw-r--r--src/util/age_restriction.c3
-rw-r--r--src/util/amount.c27
-rw-r--r--src/util/conversion.c402
-rw-r--r--src/util/crypto_confirmation.c8
-rw-r--r--src/util/crypto_contract.c42
-rw-r--r--src/util/crypto_helper_esign.c6
-rw-r--r--src/util/crypto_helper_rsa.c12
-rw-r--r--src/util/exchange_signatures.c16
-rw-r--r--src/util/iban.c6
-rw-r--r--src/util/offline_signatures.c86
-rw-r--r--src/util/payto.c6
-rw-r--r--src/util/taler-exchange-secmod-cs.c24
-rw-r--r--src/util/taler-exchange-secmod-eddsa.c6
-rw-r--r--src/util/taler-exchange-secmod-rsa.c36
-rw-r--r--src/util/test_conversion.c149
-rwxr-xr-xsrc/util/test_conversion.sh5
-rw-r--r--src/util/test_crypto.c25
-rw-r--r--src/util/util.c169
206 files changed, 8760 insertions, 3512 deletions
diff --git a/contrib/gana b/contrib/gana
-Subproject bf43b20a0362ac19bcf1bab9c33215e55d8d9f3
+Subproject 85736484cb0da26aded705ebb1e944e8bb1b850
diff --git a/contrib/gnunet.tag b/contrib/gnunet.tag
index f270ba5ea..bdc112a17 100644
--- a/contrib/gnunet.tag
+++ b/contrib/gnunet.tag
@@ -31,7 +31,13 @@
<member kind="define">
<type>#define</type>
<name>GNUNET_TIME_UNIT_FOREVER_ABS</name>
- <anchorfile>gnunet_util_lib.h</anchorfile>
+ <anchorfile>gnunet_time_lib.h</anchorfile>
+ <arglist></arglist>
+ </member>
+ <member kind="define">
+ <type>#define</type>
+ <name>GNUNET_TIME_UNIT_ZERO_ABS</name>
+ <anchorfile>gnunet_time_lib.h</anchorfile>
<arglist></arglist>
</member>
</compound>
diff --git a/doc/doxygen/taler.doxy b/doc/doxygen/taler.doxy
index 6eeb514a5..b1c8637a2 100644
--- a/doc/doxygen/taler.doxy
+++ b/doc/doxygen/taler.doxy
@@ -5,7 +5,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "GNU Taler: Exchange"
-PROJECT_NUMBER = 0.8.3
+PROJECT_NUMBER = 0.9.3
PROJECT_LOGO = logo.svg
OUTPUT_DIRECTORY = .
CREATE_SUBDIRS = YES
@@ -97,59 +97,11 @@ WARN_LOGFILE =
INPUT = ../../src
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c \
- *.cc \
- *.cxx \
- *.cpp \
- *.c++ \
- *.d \
- *.java \
- *.ii \
- *.ixx \
- *.ipp \
- *.i++ \
- *.inl \
- *.h \
- *.hh \
- *.hxx \
- *.hpp \
- *.h++ \
- *.idl \
- *.odl \
- *.cs \
- *.php \
- *.php3 \
- *.inc \
- *.m \
- *.mm \
- *.dox \
- *.py \
- *.f90 \
- *.f \
- *.vhd \
- *.vhdl \
- *.C \
- *.CC \
- *.C++ \
- *.II \
- *.I++ \
- *.H \
- *.HH \
- *.H++ \
- *.CS \
- *.PHP \
- *.PHP3 \
- *.M \
- *.MM \
- *.PY \
- *.F90 \
- *.F \
- *.VHD \
- *.VHDL
+ *.h
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS = */test_* \
- */.svn/* \
*/.git/* \
*/perf_* .* \
.* \
@@ -191,7 +143,9 @@ HTML_STYLESHEET =
GENERATE_HTMLHELP = NO
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "GNU Taler Source Documentation"
-DOCSET_BUNDLE_ID = net.taler
+DOCSET_BUNDLE_ID = net.taler.exchange
+DOCSET_PUBLISHER_ID = net.taler
+DOCSET_PUBLISHER_NAME = Taler Systems SA
HTML_DYNAMIC_SECTIONS = NO
CHM_FILE =
HHC_LOCATION =
diff --git a/doc/flows/Makefile b/doc/flows/Makefile
new file mode 100644
index 000000000..e6af0897c
--- /dev/null
+++ b/doc/flows/Makefile
@@ -0,0 +1,3 @@
+all:
+ pdflatex main.tex
+ pdflatex main.tex
diff --git a/doc/flows/fees-coins.tex b/doc/flows/fees-coins.tex
new file mode 100644
index 000000000..c24f19a28
--- /dev/null
+++ b/doc/flows/fees-coins.tex
@@ -0,0 +1,39 @@
+\section{Fees per coin} \label{sec:fees:coin}
+
+Payments with Taler are always made using coins. Each coin has a specific
+denomination, and an exchange will issue coins in different denominations (in
+the same currency). The fees per coin depend on the operation and the
+denomination.
+
+The primary fee to be paid is a {\bf deposit} fee that is
+charged whenever a coin is fully or partially deposited
+into a bank account or another wallet.
+
+A secondary fee to be paid is a {\bf change} fee that is
+charged whenever a coin partially spent and change must
+be rendered.
+
+Coins also have an {\bf expiration} date of approximately {\bf one year}.
+After the expiration date, coins become worthless. Wallets that are online
+{\bf three months} {\em before} a coin expires will automatically trade any
+such coins for one or more fresh coins with a later expiration date. This
+process is also subject to the {\bf change} fee.
+
+
+\begin{table}[h!]
+ \caption{Fees per coin. Coin denomination values are given in units of CHF 0.01.}
+ \label{table:fees:coins}
+ \begin{center}
+ \begin{tabular}{l|c|r}
+ {\bf Denomination} & {\bf Fee type} & {\bf Amount} \\ \hline \hline
+ $2^{-4}-2^{ 0}$ & deposit & {\em CHF 0.00125} \\
+ $2^{-4}-2^{ 0}$ & change & {\em CHF 0.00125} \\
+ $2^{ 0}-2^{ 3}$ & deposit & {\em CHF 0.00250} \\
+ $2^{ 0}-2^{ 3}$ & change & {\em CHF 0.00125} \\
+ $2^{ 4}-2^{ 8}$ & deposit & {\em CHF 0.005} \\
+ $2^{ 4}-2^{ 8}$ & change & {\em CHF 0.00125} \\
+ $2^{ 8}-2^{12}$ & deposit & {\em CHF 0.01} \\
+ $2^{ 8}-2^{12}$ & change & {\em CHF 0.00125} \\
+ \end{tabular}
+ \end{center}
+\end{table}
diff --git a/doc/flows/fees-wire.tex b/doc/flows/fees-wire.tex
new file mode 100644
index 000000000..214a69021
--- /dev/null
+++ b/doc/flows/fees-wire.tex
@@ -0,0 +1,30 @@
+\section{Fees per wire} \label{sec:fees:wire}
+
+Wire fees apply whenever an exchange needs to initiate a wire transfer to
+another bank account. Wire fees do not apply to every individual payment to a
+merchant, as merchants can choose to {\em aggregate} multiple micropayments
+into one large payment on the wire. Wire fees also do not apply to
+wallet-to-wallet payments within the Taler system.
+
+A {\bf wire} fee is applied when a merchant receives
+an aggregated payment into their bank account.
+
+A {\bf closing} fee is applied when a wallet fails to
+withdraw coins and money has to be sent back to the
+originating bank account.
+
+\begin{table}[h!]
+ \caption{Table with wire fees. Wire fees are set annually.}
+ \label{table:fees:wire}
+ \begin{center}
+ \begin{tabular}{l|c|r}
+ {\bf Year} & {\bf Fee type} & {\bf Amount} \\ \hline \hline
+ 2023 & wire & {\em CHF 0.05} \\
+ 2023 & closing & {\em CHF 0.10} \\
+ 2024 & wire & {\em CHF 0.05} \\
+ 2024 & closing & {\em CHF 0.10} \\
+ 2025 & wire & {\em CHF 0.05} \\
+ 2025 & closing & {\em CHF 0.10} \\
+ \end{tabular}
+ \end{center}
+\end{table}
diff --git a/doc/flows/int-deposit.tex b/doc/flows/int-deposit.tex
new file mode 100644
index 000000000..4b1f657ba
--- /dev/null
+++ b/doc/flows/int-deposit.tex
@@ -0,0 +1,52 @@
+\section{Deposit}
+
+
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{wallet}{\shortstack{Customer wallet \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{bank}{\shortstack{Customer bank \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
+ \end{tikzpicture}
+ }}
+ \postlevel
+ \begin{callself}{wallet}{Review deposit fees}{}
+ \end{callself}
+ \mess[0]{wallet}{Deposit {(Coins)}}{exchange}
+ \begin{sdblock}{Acceptable account?}{}
+ \mess[0]{exchange}{{Refuse deposit}}{wallet}
+ \end{sdblock}
+ \begin{sdblock}{KYC/AML required?}{}
+ \begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
+ \end{callself}
+ \end{sdblock}
+% \prelevel
+% \prelevel
+% \begin{sdblock}{User abort?}{}
+% \mess[0]{wallet}{{Request abort}}{exchange}
+% \mess[0]{exchange}{{Abort confirmation}}{wallet}
+% \end{sdblock}
+ \mess[0]{exchange}{{Initiate transfer}}{bank}
+
+\end{sequencediagram}
+ \caption{Deposit interactions between customer, Taler exchange (payment
+ service provider) and customer's bank.}
+ \label{fig:int:deposit}
+\end{figure}
+
+We do {\bf not} permit the customer to regain control over their funds {\em
+ unless} they pass the KYC/AML checks. The technical reason is simply that
+the KYC/AML checks happen {\em after} the aggregation logic and at this point
+refunds are no longer permitted. From a compliance perspective, this also
+prevents malicious customers from risk-free probing of the system.
diff --git a/doc/flows/int-pay.tex b/doc/flows/int-pay.tex
new file mode 100644
index 000000000..d2f0fb585
--- /dev/null
+++ b/doc/flows/int-pay.tex
@@ -0,0 +1,58 @@
+\section{Pay}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{wallet}{\shortstack{Customer wallet \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
+ \end{tikzpicture}
+ }}
+ \newinst[1]{merchant}{\shortstack{Merchant \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[1]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[1]{bank}{\shortstack{Merchant bank \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] {Commercial \\ Accounts};
+ \end{tikzpicture}
+ }}
+ \postlevel
+ \mess[0]{wallet}{Browse catalog}{merchant}
+ \mess[0]{merchant}{Commercial offer}{wallet}
+ \begin{callself}{wallet}{Review offer}{}
+ \end{callself}
+ \mess[0]{wallet}{Send payment {(Coins)}}{merchant}
+ \mess[0]{merchant}{Deposit {(Coins)}}{exchange}
+ \begin{sdblock}{Acceptable account?}{}
+ \mess[0]{exchange}{{Refuse deposit}}{merchant}
+ \mess[0]{merchant}{{Refund purchase}}{wallet}
+ \end{sdblock}
+ \mess[0]{exchange}{{Confirm deposit}}{merchant}
+ \mess[0]{merchant}{Fulfill order}{wallet}
+ \begin{callself}{exchange}{Aggregate transactions}{}
+ \end{callself}
+ \begin{sdblock}{KYC/AML required?}{}
+ \begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
+ \end{callself}
+ \end{sdblock}
+ \mess[0]{exchange}{{Initiate transfer}}{bank}
+ \end{sequencediagram}
+ \caption{Deposit interactions between customer, merchant,
+ Taler exchange (payment service provider) and merchant bank.}
+ \label{fig:int:pay}
+\end{figure}
+
+{\bf Internal note:} The exchange refusing a deposit immediately based on
+unaccaptable merchant accounts may not be fully implemented (this is a very
+recent feature, after all); especially the merchant then automatically
+refunding the purchase to the customer is certainly missing. However,
+the entire situation only arises when a merchant is incorrectly configured
+and in violation of the terms of service.
diff --git a/doc/flows/int-pull.tex b/doc/flows/int-pull.tex
new file mode 100644
index 000000000..8c9b66b1b
--- /dev/null
+++ b/doc/flows/int-pull.tex
@@ -0,0 +1,55 @@
+\section{Pull payment (aka invoicing)}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{payer}{\shortstack{Payer \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] {Pre-funded \\ Wallet};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{payee}{\shortstack{Payee \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
+ \end{tikzpicture}
+ }}
+ \postlevel
+ \begin{callself}{payee}{Review pull payment fees}{}
+ \end{callself}
+ \mess[0]{payee}{{Create invoice (Wallet ID)}}{exchange}
+
+ \mess[0]{exchange}{{Invoice ready}}{payee}
+ \mess[0]{payee}{{Send invoice (e.g. via QR code)}}{payer}
+
+ \begin{callself}{payer}{Review invoice}{}
+ \end{callself}
+ \mess[0]{payer}{{Make payment (Coins)}}{exchange}
+
+ \begin{sdblock}{Domestic wallet?}{}
+ \begin{callself}{exchange}{Figure~\ref{fig:proc:domestic}}{}
+ \end{callself}
+ \end{sdblock}
+ \begin{sdblock}{KYC/AML required?}{}
+ \begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
+ \end{callself}
+ \end{sdblock}
+
+ \mess[0]{exchange}{{Distribute digital cash}}{payee}
+
+\end{sequencediagram}
+ \caption{Interactions between wallets and Taler exchange
+ in a pull payment.}
+ \label{fig:int:pull}
+\end{figure}
+
+We do {\bf not} permit the payer to regain control over their funds, once the
+payment was made they are locked {\em until} the payee passes the KYC/AML
+checks. We only do the AML/KYC process once the funds are locked at the
+exchange. This ensures we know the actual transacted amounts (which may be
+lower than the total amounts requested) and prevents risk-free probing
+attacks.
diff --git a/doc/flows/int-push.tex b/doc/flows/int-push.tex
new file mode 100644
index 000000000..fd49e8d40
--- /dev/null
+++ b/doc/flows/int-push.tex
@@ -0,0 +1,47 @@
+\section{Push payment}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{payer}{\shortstack{Payer \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] {Pre-funded \\ Wallet};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{payee}{\shortstack{Payee \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
+ \end{tikzpicture}
+ }}
+ \postlevel
+ \begin{callself}{payer}{Review push payment fees}{}
+ \end{callself}
+ \mess[0]{payer}{{Push funds (Coins)}}{exchange}
+ \mess[0]{payer}{{Offer payment (e.g. via QR code)}}{payee}
+ \begin{callself}{payee}{Review payment offer}{}
+ \end{callself}
+ \mess[0]{payee}{{Request funds (Wallet ID)}}{exchange}
+ \begin{sdblock}{Domestic wallet?}{}
+ \begin{callself}{exchange}{Figure~\ref{fig:proc:domestic}}{}
+ \end{callself}
+ \end{sdblock}
+ \begin{sdblock}{KYC/AML required?}{}
+ \begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
+ \end{callself}
+ \end{sdblock}
+ \mess[0]{exchange}{{Distribute digital cash}}{payee}
+% \postlevel
+ \begin{sdblock}{Payment offer expired?}{}
+ \mess[0]{exchange}{{Return funds}}{payer}
+ \end{sdblock}
+
+\end{sequencediagram}
+ \caption{Interactions between wallets and Taler exchange
+ in a push payment.}
+ \label{fig:int:push}
+\end{figure}
diff --git a/doc/flows/int-refund.tex b/doc/flows/int-refund.tex
new file mode 100644
index 000000000..3f11e5600
--- /dev/null
+++ b/doc/flows/int-refund.tex
@@ -0,0 +1,39 @@
+\section{Refund}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{wallet}{\shortstack{Customer wallet \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{merchant}{\shortstack{Merchant \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \postlevel
+ \begin{callself}{merchant}{Initiate refund}{}
+ \end{callself}
+ \mess[0]{merchant}{{Refund offer (QR code)}}{wallet}
+ \mess[0]{wallet}{Download refund}{merchant}
+ \mess[0]{merchant}{Approve refund}{exchange}
+ \mess[0]{exchange}{Confirm refund}{merchant}
+ \mess[0]{merchant}{Return refund confirmation}{wallet}
+ \end{sequencediagram}
+ \caption{Refund processing when a merchant is unable to fulfill
+ a contract. Refunds must happen {\em before} the
+ exchange has aggregated the original transaction for
+ a bank transfer to the merchant. Furthermore, refunds
+ can only go to the customer who made the original payment
+ and the refund cannot exceed the amount of the original
+ payment.}
+ \label{fig:int:refund}
+\end{figure}
diff --git a/doc/flows/int-shutdown.tex b/doc/flows/int-shutdown.tex
new file mode 100644
index 000000000..e8ee6feed
--- /dev/null
+++ b/doc/flows/int-shutdown.tex
@@ -0,0 +1,48 @@
+\section{Shutdown}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{wallet}{\shortstack{Customer wallet \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{bank}{\shortstack{Customer bank \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
+ \end{tikzpicture}
+ }}
+ \postlevel
+
+ \begin{callself}{exchange}{Operator initiates shutdown}{}
+ \end{callself}
+ \mess[0]{exchange}{{Shutdown alert}}{wallet}
+ \begin{sdblock}{Bank account known?}{}
+ \begin{callself}{wallet}{Designate bank account}{}
+ \end{callself}
+ \end{sdblock}
+ \mess[0]{wallet}{{Deposit (Coins)}}{exchange}
+ \begin{sdblock}{Acceptable account?}{}
+ \mess[0]{exchange}{{Refuse deposit}}{wallet}
+ \end{sdblock}
+ \begin{sdblock}{KYC/AML required?}{}
+ \begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
+ \end{callself}
+ \end{sdblock}
+ \mess[0]{exchange}{{Initiate transfer}}{bank}
+\end{sequencediagram}
+ \caption{Shutdown interactions between customer, Taler exchange (payment
+ service provider) and bank.}
+ \label{fig:int:shutdown}
+\end{figure}
+
+KYC/AML requirements are relaxed in cases where the customer is able to
+cryptographically demonstrate that they previously withdrew these coins from
+the designated checking account. Thus, KYC/AML checks here primarily still
+apply if the customer received the funds via P2P transfers from other wallets.
diff --git a/doc/flows/int-withdraw.tex b/doc/flows/int-withdraw.tex
new file mode 100644
index 000000000..9b044df67
--- /dev/null
+++ b/doc/flows/int-withdraw.tex
@@ -0,0 +1,49 @@
+\section{Withdraw}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{wallet}{\shortstack{Customer wallet \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{bank}{\shortstack{Customer bank \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
+ \end{tikzpicture}
+ }}
+ \postlevel
+ \mess[0]{wallet}{Withdraw {(Amount)}}{exchange}
+ \mess[0]{exchange}{{Configuration (ToS, Fees)}}{wallet}
+ \begin{sdblock}{once}{}
+ \begin{callself}{wallet}{Accept ToS}{}
+ \end{callself}
+ \end{sdblock}
+ \begin{callself}{wallet}{Review withdraw fees}{}
+ \end{callself}
+ \mess[0]{wallet}{{Initiate transfer (Amount, Credit account, Wallet ID)}}{bank}
+ \mess[0]{bank}{{Credit (Wallet ID)}}{exchange}
+
+ \begin{sdblock}{Acceptable transfer?}{}
+ \mess[0]{exchange}{{Bounce funds}}{bank}
+ \end{sdblock}
+ \postlevel
+ \mess[0]{exchange}{Confirm wire transfer}{wallet}
+ \mess[0]{wallet}{Request digital cash}{exchange}
+ \mess[0]{exchange}{Distribute digital cash}{wallet}
+ \postlevel
+ \begin{sdblock}{Withdraw period expired?}{}
+ \mess[0]{exchange}{{Return remaining funds}}{bank}
+ \end{sdblock}
+\end{sequencediagram}
+ \caption{Withdraw interactions between customer, Taler exchange (payment
+ service provider) and bank. The amount of digital cash distributed is
+ subject to limits per origin account (see Figure~\ref{fig:kyc:withdraw}).}
+ \label{fig:int:withdraw}
+\end{figure}
diff --git a/doc/flows/kyc-balance.tex b/doc/flows/kyc-balance.tex
new file mode 100644
index 000000000..1192021b3
--- /dev/null
+++ b/doc/flows/kyc-balance.tex
@@ -0,0 +1,57 @@
+\section{KYC: Balance}
+
+Note: this process is not implemented and would require non-trivial extra work
+if required.
+
+\begin{figure}[h!]
+ \begin{center}
+\begin{tikzpicture}[node distance=1cm,font=\sffamily,
+ start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
+ end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
+ process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
+ failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
+ io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
+ decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
+ arr/.style={very thick,-latex},
+ every edge quotes/.style = {auto, font=\footnotesize, sloped}
+ ]
+ \node (start) [start] {Start};
+ \node (balance) [decision,below=of start,text width=3cm] {Transaction leaves wallet balance below AML threshold?};
+ \node (registered) [decision,below=of balance,text width=3cm] {Wallet has been subject to KYC?};
+ \node (kyc) [process, below=of registered] {KYC process};
+ \node (aml) [process, left=of kyc] {AML process};
+ \node (allow) [end, right=of balance] {Allow};
+ \node (deny) [failed, right=of registered] {Deny};
+ \draw[arr] (start) -> (balance) {};
+ \draw[arr] (balance) -> (registered);
+ \draw (balance) edge["No"] (registered);
+ \draw[arr] (balance) -> (allow);
+ \draw (balance) edge["Yes"] (allow);
+
+ \draw[arr] (registered) -> (kyc);
+ \draw (registered) edge["No"] (kyc);
+ \draw[arr] (registered) -> (deny);
+ \draw (registered) edge["Yes"] (deny);
+
+ \draw[arr] (kyc) -> (deny);
+ \draw (kyc) edge["Failed"] (deny);
+ \draw[arr] (kyc) -> (aml);
+ \draw (kyc) edge["Ok"] (aml);
+
+ \draw[arr] (aml) -> (balance.west);
+ \draw (aml) edge["New threshold"] (balance.west);
+\end{tikzpicture}
+ \end{center}
+ \caption{Regulatory process when a wallet exceeds its AML threshold.
+ When the transfer is denied the transaction (withdraw, P2P transfer)
+ is refused by the wallet.}
+\end{figure}
+
+
+\begin{table}[h!]
+ \caption{Settings for the balance trigger}
+ \begin{tabular}{l|l|r}
+ {\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
+ Default AML threshold & Amount & {\em 1000 CHF} \\
+ \end{tabular}
+\end{table}
diff --git a/doc/flows/kyc-deposit.tex b/doc/flows/kyc-deposit.tex
new file mode 100644
index 000000000..2423235ab
--- /dev/null
+++ b/doc/flows/kyc-deposit.tex
@@ -0,0 +1,71 @@
+\section{KYC: Deposit}
+
+\begin{figure}[h!]
+ \begin{center}
+\begin{tikzpicture}[node distance=1cm,font=\sffamily,
+ start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
+ end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
+ process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
+ failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
+ io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
+ decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
+ arr/.style={very thick,-latex},
+ every edge quotes/.style = {auto, font=\footnotesize, sloped}
+ ]
+ \node (start) [start] {Start};
+ \node (country) [decision,below=of start,text width=2.5cm] {Target account in allowed country?};
+ \node (amount) [decision, below=of country,text width=2.5cm] {Target account received less than KYC threshold?};
+ \node (kyc) [process, right=of amount] {KYC process};
+ \node (high) [decision, below=of amount,text width=2.5cm] {Target account received more than its AML threshold?};
+ \node (aml) [process, right=of high] {AML process};
+ \node (dummy) [below right=of aml] {};
+ \node (allow) [end, below right=of dummy] {Allow};
+ \node (deny) [failed, right=of kyc] {Deny};
+ \draw[arr] (start) -> (country) {};
+
+ \draw[arr] (country) -> (amount);
+ \draw (country) edge["Yes"] (amount);
+
+ \draw[arr] (country.east) -> (deny);
+ \draw (country.east) edge["No"] (deny);
+
+ \draw[arr] (amount) -> (high);
+ \draw (amount) edge["Yes"] (high);
+
+ \draw[arr] (amount.east) -> (kyc);
+ \draw (amount.east) edge["No"] (kyc);
+
+ \draw[arr] (kyc) -> (deny);
+ \draw (kyc) edge["Failed"] (deny);
+
+ \draw[arr] (kyc) -> (high);
+ \draw (kyc) edge["Succeeded"] (high);
+
+ \draw[arr] (high.south) -> (allow);
+ \draw (high.south) edge["Yes"] (allow);
+
+ \draw[arr] (high.east) -> (aml);
+ \draw (high.east) edge["No"] (aml);
+
+ \draw[arr] (aml) -> (deny);
+ \draw (aml) edge["Violation"] (deny);
+
+ \draw[arr] (aml) -> (allow);
+ \draw (aml) edge["Ok"] (allow);
+\end{tikzpicture}
+ \end{center}
+ \caption{Regulatory process when depositing digital cash into a bank
+ account. When the transfer is denied, the money is returned to the
+ originating wallet.}
+\end{figure}
+
+
+\begin{table}[h!]
+ \caption{Settings for the deposit trigger}
+ \begin{tabular}{l|l|r}
+ {\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
+ Allowed bank accounts & RFC 8905 RegEx & {\em CH*} \\ \hline
+ KYC deposit threshold & Amount & {\em 1000 CHF} \\
+ Default AML deposit threshold & Amount & {\em 2500 CHF} \\
+ \end{tabular}
+\end{table}
diff --git a/doc/flows/kyc-pull.tex b/doc/flows/kyc-pull.tex
new file mode 100644
index 000000000..b7cd34477
--- /dev/null
+++ b/doc/flows/kyc-pull.tex
@@ -0,0 +1,78 @@
+\section{KYC/AML: Pull Payment}
+
+\begin{figure}[h!]
+ \begin{center}
+\begin{tikzpicture}[node distance=0.9cm,font=\sffamily,
+ start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
+ end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
+ process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
+ failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
+ io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
+ decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
+ arr/.style={very thick,-latex},
+ every edge quotes/.style = {auto, font=\footnotesize, sloped}
+ ]
+ \node (start) [start] {Start};
+ \node (wallet) [decision,below=of start,text width=2.5cm] {Wallet linked to (domestic) phone number?};
+ \node (domestic) [process, right=of wallet] {Validate phone number};
+ \node (amount) [decision, below=of wallet,text width=2.5cm] {Wallet received less than KYC threshold from other wallets?};
+ \node (kyc) [process, right=of amount] {KYC process};
+ \node (high) [decision, below=of amount,text width=2.5cm] {Wallet received more than its AML threshold?};
+ \node (aml) [process, right=of high] {AML process};
+ \node (dummy) [below right=of aml] {};
+ \node (allow) [end, below right=of dummy] {Allow invoicing};
+ \node (deny) [failed, right=of kyc] {Deny};
+ \draw[arr] (start) -> (wallet) {};
+
+ \draw[arr] (wallet) -> (amount);
+ \draw (wallet) edge["Yes"] (amount);
+
+ \draw[arr] (wallet.east) -> (domestic);
+ \draw (wallet.east) edge["No"] (domestic);
+
+ \draw[arr] (domestic) -> (amount);
+ \draw (domestic) edge["Confirmed"] (amount);
+
+ \draw[arr] (domestic) -> (deny);
+ \draw (domestic) edge["Failed"] (deny);
+
+ \draw[arr] (amount) -> (high);
+ \draw (amount) edge["Yes"] (high);
+
+ \draw[arr] (amount.east) -> (kyc);
+ \draw (amount.east) edge["No"] (kyc);
+
+ \draw[arr] (kyc) -> (deny);
+ \draw (kyc) edge["Failed"] (deny);
+
+ \draw[arr] (kyc) -> (high);
+ \draw (kyc) edge["Succeeded"] (high);
+
+ \draw[arr] (high.south) -> (allow);
+ \draw (high.south) edge["Yes"] (allow);
+
+ \draw[arr] (high.east) -> (aml);
+ \draw (high.east) edge["No"] (aml);
+
+ \draw[arr] (aml) -> (deny);
+ \draw (aml) edge["Violation"] (deny);
+
+ \draw[arr] (aml) -> (allow);
+ \draw (aml) edge["Ok"] (allow);
+\end{tikzpicture}
+ \end{center}
+ \caption{Regulatory process when receiving payments from another wallet.
+ The threshold depends on the risk profile from the KYC process.
+ When invoicing is denied the wallet cannot generate the invoice.}
+\end{figure}
+
+
+\begin{table}[h!]
+ \caption{Settings for the pull payment trigger}
+ \begin{tabular}{l|l|r}
+ {\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
+ Permitted phone numbers & Dialing prefix & {\em +41} \\
+ P2P KYC threshold & Amount & {\em 100 CHF} \\
+ Default P2P AML threshold & Amount & {\em 1000 CHF} \\
+ \end{tabular}
+\end{table}
diff --git a/doc/flows/kyc-push.tex b/doc/flows/kyc-push.tex
new file mode 100644
index 000000000..6d25ac7ef
--- /dev/null
+++ b/doc/flows/kyc-push.tex
@@ -0,0 +1,79 @@
+\section{KYC/AML: Push Payment}
+
+\begin{figure}[h!]
+ \begin{center}
+\begin{tikzpicture}[node distance=0.9cm,font=\sffamily,
+ start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
+ end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
+ process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
+ failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
+ io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
+ decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
+ arr/.style={very thick,-latex},
+ every edge quotes/.style = {auto, font=\footnotesize, sloped}
+ ]
+ \node (start) [start] {Start};
+ \node (wallet) [decision,below=of start,text width=2.5cm] {Wallet linked to (domestic) phone number?};
+ \node (domestic) [process, right=of wallet] {Validate phone number};
+ \node (amount) [decision, below=of wallet,text width=2.5cm] {Wallet received less than KYC threshold from other wallets?};
+ \node (kyc) [process, right=of amount] {KYC process};
+ \node (high) [decision, below=of amount,text width=2.5cm] {Wallet received more than its AML threshold?};
+ \node (aml) [process, right=of high] {AML process};
+ \node (dummy) [below right=of aml] {};
+ \node (allow) [end, below right=of dummy] {Allow};
+ \node (deny) [failed, right=of kyc] {Deny};
+ \draw[arr] (start) -> (wallet) {};
+
+ \draw[arr] (wallet) -> (amount);
+ \draw (wallet) edge["Yes"] (amount);
+
+ \draw[arr] (wallet.east) -> (domestic);
+ \draw (wallet.east) edge["No"] (domestic);
+
+ \draw[arr] (domestic) -> (amount);
+ \draw (domestic) edge["Confirmed"] (amount);
+
+ \draw[arr] (domestic) -> (deny);
+ \draw (domestic) edge["Failed"] (deny);
+
+ \draw[arr] (amount) -> (high);
+ \draw (amount) edge["Yes"] (high);
+
+ \draw[arr] (amount.east) -> (kyc);
+ \draw (amount.east) edge["No"] (kyc);
+
+ \draw[arr] (kyc) -> (deny);
+ \draw (kyc) edge["Failed"] (deny);
+
+ \draw[arr] (kyc) -> (high);
+ \draw (kyc) edge["Succeeded"] (high);
+
+ \draw[arr] (high.south) -> (allow);
+ \draw (high.south) edge["Yes"] (allow);
+
+ \draw[arr] (high.east) -> (aml);
+ \draw (high.east) edge["No"] (aml);
+
+ \draw[arr] (aml) -> (deny);
+ \draw (aml) edge["Violation"] (deny);
+
+ \draw[arr] (aml) -> (allow);
+ \draw (aml) edge["Ok"] (allow);
+\end{tikzpicture}
+ \end{center}
+ \caption{Regulatory process when receiving payments from another wallet.
+ The threshold depends on the risk profile from the KYC process.
+ When the transfer is denied the money is (eventually) returned to
+ the originating wallet.}
+\end{figure}
+
+
+\begin{table}[h!]
+ \caption{Settings for the push payment trigger}
+ \begin{tabular}{l|l|r}
+ {\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
+ Permitted phone numbers & Dialing prefix & {\em +41} \\
+ P2P KYC threshold & Amount & {\em 100 CHF} \\
+ Default P2P AML threshold & Amount & {\em 1000 CHF} \\
+ \end{tabular}
+\end{table}
diff --git a/doc/flows/kyc-withdraw.tex b/doc/flows/kyc-withdraw.tex
new file mode 100644
index 000000000..ecdc9a399
--- /dev/null
+++ b/doc/flows/kyc-withdraw.tex
@@ -0,0 +1,45 @@
+\section{KYC: Withdraw}
+
+\begin{figure}[h!]
+ \begin{center}
+\begin{tikzpicture}[node distance=1cm,font=\sffamily,
+ start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
+ end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
+ process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
+ failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
+ io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
+ decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
+ arr/.style={very thick,-latex},
+ every edge quotes/.style = {auto, font=\footnotesize, sloped}
+ ]
+ \node (start) [start] {Start};
+ \node (country) [decision,below=of start,text width=3cm] {Wire transfer originates from allowed country?};
+ \node (amount) [decision, below=of country,text width=3cm] {Transferred less than maximum amount from origin account over last month?};
+ \node (allow) [end, below=of amount] {Allow};
+ \node (deny) [failed, right=of allow] {Deny};
+ \draw[arr] (start) -> (country) {};
+ \draw[arr] (country) -> (amount);
+ \draw (country) edge["Yes"] (amount);
+ \draw[arr] (country.east) -> (deny);
+ \draw (country.east) edge["No"] (deny);
+ \draw[arr] (amount) -> (allow);
+ \draw (amount) edge["Yes"] (allow);
+ \draw[arr] (amount.east) -> (deny);
+ \draw (amount.east) edge["No"] (deny);
+\end{tikzpicture}
+ \end{center}
+ \caption{Regulatory process when withdrawing digital cash from a
+ bank account.
+ When the transfer is denied the money is (eventually) returned to
+ the originating bank account.}
+ \label{fig:kyc:withdraw}
+\end{figure}
+
+\begin{table}[h!]
+ \caption{Settings for the withdraw trigger}
+ \begin{tabular}{l|l|r}
+ {\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
+ Allowed bank accounts & RFC 8905 RegEx & {\em CH*} \\ \hline
+ Monthly withdraw maximum & Amount & {\em 1000 CHF} \\
+ \end{tabular}
+\end{table}
diff --git a/doc/flows/main.tex b/doc/flows/main.tex
new file mode 100644
index 000000000..c2aee65ac
--- /dev/null
+++ b/doc/flows/main.tex
@@ -0,0 +1,159 @@
+\documentclass[10pt,a4paper,oneside]{book}
+\usepackage[utf8]{inputenc}
+\usepackage{url}
+\usepackage{graphicx}
+\usepackage{hyperref}
+\usepackage{qrcode}
+\usepackage{pgf-umlsd}
+\usepackage{tikz}
+\usetikzlibrary{shapes,arrows}
+\usetikzlibrary{positioning}
+\usetikzlibrary{calc}
+\usetikzlibrary{quotes}
+\author{Christian Grothoff}
+\title{Flows in the GNU Taler System}
+
+\begin{document}
+
+\tableofcontents
+
+\chapter{Interactions} \label{chap:interactions}
+
+This chapter introduces the main payment interactions in the GNU Taler payment
+system. For each interaction, we introduce the parties involved and in which
+order they interact and how. In each interaction it is possible that the
+Taler exchange needs to trigger a compliance process. These regulatory
+riggers are described in more detail in Chapter~\ref{chap:triggers}.
+
+The main interactions of the system are:
+
+\begin{description}
+ \item[withdraw] a customer withdraws digital cash to their wallet
+ \item[deposit] a customer returns digital cash into their bank account
+ \item[pay] a customer pays into bank account of a merchant
+ \item[refund] a merchant decides to return funds to a customer
+ \item[push] a customer sends a payment to another wallet
+ \item[pull] a customer requests a payment from another wallet (effectively sending an invoice)
+ \item[shutdown] the Taler payment system operator informs the customers that the system is being shut down for good
+\end{description}
+
+Taler has no accounts (this is digital cash) and thus there is no ``opening''
+or ``closing'' of accounts. The equivalent of ``opening'' an account is thus
+to withdraw digital cash. The equivalent of ``closing'' an account is to
+either (1) deposit the funds explicitly into a bank account, or (2) the coins
+will expire if the wallet was lost (including long-term offline or
+uninstalled). Finally, if a wallet remains (occasionally) online but a user
+does simply not spend the coins will (3) diminish in value from the change
+fees (see Section~\ref{sec:fees:coin}) that apply to prevent the coins from
+expiring outright.
+
+The following sections describe the respective processes for each of these
+interactions.
+
+\include{int-withdraw}
+\include{int-deposit}
+\include{int-pay}
+\include{int-refund}
+\include{int-push}
+\include{int-pull}
+\include{int-shutdown}
+
+
+\chapter{Regulatory Triggers} \label{chap:triggers}
+
+In this chapter we show decision diagrams for regulatory processes of the
+various core operations of the GNU Taler payment system. In each case, the
+{\bf start} state refers to one of the interactions described in the previous
+chapter. The payment system will then use the process to arrive at an {\bf
+ allow} decision which permits the transaction to go through, or at a {\bf
+ deny} decision which ensures that the funds are not moved.
+
+The specific {\em decisions} (in green) depend on the risk profile and the
+regulatory environment. The tables in each section list the specific values
+that are to be configured.
+
+There are five types if interactions that can trigger regulatory processes:
+
+\begin{description}
+ \item[withdraw] a customer withdraws digital cash from their {\bf bank account}
+ \item[deposit] a merchant's {\bf bank account} is designated to receive a payment in digital cash
+ \item[push] a {\bf wallet} accepts a payment from another wallet
+ \item[pull] a {\bf wallet} requests a payment from another wallet
+ \item[balance] a withdraw or P2P payment causes the balance of a {\bf wallet} to exceed a given threshold
+\end{description}
+
+We note in bold the {\bf anchor} for the regulator process. The anchor is used
+to link the interaction to an identity. Once an identity has been established
+for a particular anchor, that link is considered established for all types of
+activities involving that anchor. A wallet is uniquely identified in the
+system by its unique cryptographic key. A bank account is uniquely identified
+in the system by its (RFC 8905) bank routing data (usually including BIC, IBAN
+and account owner name).
+
+The KYC and AML processes themselves are described in
+Chapter~\ref{chap:regproc}.
+
+\include{kyc-withdraw}
+\include{kyc-deposit}
+\include{kyc-push}
+\include{kyc-pull}
+\include{kyc-balance}
+
+\chapter{Regulatory Processes} \label{chap:regproc}
+
+This chapter describes the interactions between the customer, exchange and
+organizations or staff assisting with regulatory processes designed to ensure
+that customers are residents in the area of operation of the payment service
+provider, are properly identified, and do not engage in money laundering.
+
+The three main regulatory processes are:
+
+\begin{description}
+\item[domestic check] This process establishes that a user is generally
+ eligible to use the payment system. The process checks that the user has an
+ eligible address, but stops short of establishing the user's identity.
+\item[kyc] This process establishes a user's legal identity, possibly
+ using external providers to review documents and check against blacklists.
+\item[aml] The AML process reviews suspicious payment activities for
+ money laundering. Here AML staff reviews all collected information.
+\end{description}
+
+\include{proc-domestic}
+\include{proc-kyc}
+\include{proc-aml}
+
+\chapter{Fees} \label{chap:fees}
+
+The business model for operating a Taler exchange is to charge transaction
+fees. Fees are charged on certain operations by the exchange. There are two
+types of fees, {\bf wire fees} and {\bf coin fees}. This chapter describes
+the fee structure.
+
+Fixed, amount-independent {\bf wire fees} are charged on wire transfers using
+the core banking system. Details on wire fees are described in
+Section~\ref{sec:fees:wire}.
+
+Coin fees are more complex, as they do not exactly follow neither the usual
+percentage of volume model of other payment systems. Instead, coin fees are
+applied per coin, resulting in a {\em logarithmic} fee structure. As a
+result, the effective fee {\em percentage} for tiny transactions is high (for
+example 50\% for transactions of 0.0025 CHF) while the effective fee
+percentage for large transactions is nominal (for example $\approx$ 0.05\% for
+transactions of $\approx$ 40 CHF). Details on coin fees are described in
+Section~\ref{sec:fees:coin}.
+
+Fees are configurable (and that fee types beyond those described here are
+supported by the software). Thus, the specific fees may be adjusted in the
+future based on business decisions. However, changes to the fees are never
+retroactively applied to coins already in circulation. Wire fees that have
+been publicly announced for a particular time period also cannot be changed.
+Finally, any change to the terms of service must also be explicitly accepted
+by the users before they withdraw additional funds.
+
+
+\include{fees-wire}
+\include{fees-coins}
+%\include{fees-other}
+
+
+\end{document}
diff --git a/doc/flows/proc-aml.tex b/doc/flows/proc-aml.tex
new file mode 100644
index 000000000..04700faf8
--- /dev/null
+++ b/doc/flows/proc-aml.tex
@@ -0,0 +1,47 @@
+\section{AML process}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{wallet}{\shortstack{Customer \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{staff}{\shortstack{AML staff \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Access \\ Token};
+ \end{tikzpicture}
+ }}
+ \postlevel
+ \mess[0]{wallet}{{Initial action}}{exchange}
+ \begin{callself}{exchange}{Establish AML requirement}{}
+ \end{callself}
+ \begin{callself}{exchange}{Queue AML task}{}
+ \end{callself}
+ \mess[0]{exchange}{Wait for AML}{wallet}
+ \mess[0]{staff}{Request AML work}{exchange}
+ \mess[0]{exchange}{{Open AML task(s)}}{staff}
+ \mess[0]{staff}{Request details}{exchange}
+ \mess[0]{exchange}{KYC/AML data}{staff}
+ \begin{callself}{staff}{Review and decide}{}
+ \end{callself}
+ \mess[0]{staff}{{Decision documentation}}{exchange}
+ \mess[0]{exchange}{AML decision}{wallet}
+ \mess[0]{wallet}{{Retry action}}{exchange}
+\end{sequencediagram}
+ \caption{Deposit interactions between customer, Taler exchange (payment
+ service provider) and the AML staff. The process can be
+ triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.
+ AML staff interactions are cryptographically secured and
+ decisions and the provided reasoning are archived by the exchange.
+ AML staff may interact with the customer (out-of-band)
+ in its decision process.
+ }
+ \label{fig:proc:aml}
+\end{figure}
diff --git a/doc/flows/proc-domestic.tex b/doc/flows/proc-domestic.tex
new file mode 100644
index 000000000..387b964d5
--- /dev/null
+++ b/doc/flows/proc-domestic.tex
@@ -0,0 +1,66 @@
+\section{Domestic wallet check}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{wallet}{\shortstack{Customer wallet \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{sms}{\shortstack{Address validator}}
+
+ \postlevel
+ \mess[0]{wallet}{{P2P payment (Wallet ID)}}{exchange}
+ \begin{callself}{exchange}{New wallet?}{}
+ \end{callself}
+ \mess[0]{exchange}{Request address validation}{sms}
+ \mess[0]{sms}{Validation process ID}{exchange}
+ \mess[0]{exchange}{Request address validation}{wallet}
+ \mess[0]{wallet}{Send address}{sms}
+ \mess[0]{sms}{{Send confirmation code (to address)}}{wallet}
+ \mess[0]{wallet}{Supply confirmation code}{sms}
+ \mess[0]{sms}{{Confirmed customer address}}{exchange}
+ \mess[0]{exchange}{{Confirm completion}}{wallet}
+ \mess[0]{wallet}{{Retry action}}{exchange}
+\end{sequencediagram}
+ \caption{Deposit interactions between customer, Taler exchange (payment
+ service provider) and external address validation service. The process can be
+ triggered by wallet-to-wallet (P2P) payments described in Chapter~\ref{chap:triggers}.}
+ \label{fig:proc:domestic}
+\end{figure}
+
+Our users have to accept the terms of service which restrict the use of the
+service to domestic customers. For interactions with the core banking system,
+this simply means that we only accept payments from or to domestic bank
+accounts. For P2P payments between wallets, we require that the wallets are
+controlled by a domestic entity. We define domestic entities as those that
+are able to receive messages at a domestic address. Two types of addresses are
+supported:
+
+\begin{itemize}
+\item Control over a domestic {\bf mobile phone number} is established
+ by sending an SMS message with a confirmation code to the MSIN.
+\item Control over a domestic {\bf postal address} is established by
+ sending a letter with a confirmation code to the address.
+\end{itemize}
+
+Depending on the type of address, a validation has a limited validity period,
+as shown in Table~\ref{table:proc:domestic}. When the validity period is
+over, a wallet has to re-do the address validation before they can receive any
+further funds through the service.
+
+\begin{table}[h!]
+ \caption{Restrictions on address validations}
+ \label{table:proc:domestic}
+ \begin{tabular}{l|l|r}
+ {\bf Type} & {\bf Validity period} & {\bf Restricted to} \\ \hline \hline
+ Mobile phone number & 12 months & {\em +41} \\
+ Postal address & 36 months & {\em Switzerland} \\
+ \end{tabular}
+\end{table}
diff --git a/doc/flows/proc-kyc.tex b/doc/flows/proc-kyc.tex
new file mode 100644
index 000000000..006a05561
--- /dev/null
+++ b/doc/flows/proc-kyc.tex
@@ -0,0 +1,88 @@
+\section{KYC process}
+
+\begin{figure}[h!]
+ \begin{sequencediagram}
+ \newinst{wallet}{\shortstack{Customer \\
+ \\ \begin{tikzpicture}
+ \node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{exchange}{\shortstack{Taler (exchange) \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+ \newinst[2]{kyc}{\shortstack{KYC provider \\
+ \\ \begin{tikzpicture}[shape aspect=.5]
+ \tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
+ \node at (1.5,0) {\shortstack{{{\tiny Database}}}};
+ \end{tikzpicture}
+ }}
+
+ \postlevel
+ \mess[0]{wallet}{{Initial action}}{exchange}
+ \begin{callself}{exchange}{Establish KYC requirement}{}
+ \end{callself}
+ \mess[0]{exchange}{Request new KYC process}{kyc}
+ \mess[0]{kyc}{{Process identifier (PI)}}{exchange}
+ \mess[0]{exchange}{{KYC required (PI)}}{wallet}
+ \mess[0]{wallet}{{KYC start (PI)}}{kyc}
+ \mess[0]{kyc}{{Request identity documentation}}{wallet}
+ \mess[0]{wallet}{{Upload identity documentation}}{kyc}
+ \begin{callself}{kyc}{Validate documentation}{}
+ \end{callself}
+ \mess[0]{kyc}{{Share documentation (PI)}}{exchange}
+ \mess[0]{kyc}{{Confirm completion}}{wallet}
+ \mess[0]{wallet}{{Retry action}}{exchange}
+\end{sequencediagram}
+ \caption{Deposit interactions between customer, Taler exchange (payment
+ service provider) and external KYC provider. The process can be
+ triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.}
+ \label{fig:proc:kyc}
+\end{figure}
+
+At the beginning of the KYC process, the user needs to specify
+whether they are an {\bf individual} or a {\bf business}. This
+then determines which types of attributes are collected in the
+KYC process (Table~\ref{table:proc:kyc:individual} vs.
+Table~\ref{table:proc:kyc:business}).
+
+\begin{table}
+ \caption{Information collected for individuals}
+ \label{table:proc:kyc:individual}
+ \begin{center}
+ \begin{tabular}{l|c|r}
+ {\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
+ Surname & yes & Mustermann \\
+ First name(s) & yes & Max \\
+ Date of birth & yes & 1.1.1980 \\
+ Nationality & yes & Swiss \\
+ Actual address of domicile & yes & Seestrasse 3, 8008 Zuerich \\
+ Phone number & no & +41-123456789 \\
+ E-mail & no & me@example.com \\
+ Identification document & yes & JPG image \\
+ \end{tabular}
+ \end{center}
+\end{table}
+
+
+\begin{table}
+ \caption{Information collected for businesses}
+ \label{table:proc:kyc:business}
+ \begin{center}
+ \begin{tabular}{l|c|r}
+ {\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
+ Company name & yes & Mega AG \\
+ Registered office & yes & Seestrasse 4, 8008 Zuerich \\
+ Company identification document & yes & PDF file \\ \hline
+ Contact person name & yes & Max Mustermann \\
+ Phone number & no & +41-123456789 \\
+ E-mail & yes & me@example.com \\
+ Identification document & yes & JPG image \\
+ Date of birth & yes & 1.1.1980 \\
+ Nationality & yes & Swiss \\ \hline
+ Power of attorney arrangement & yes & PDF file \\
+ \end{tabular}
+ \end{center}
+\end{table}
diff --git a/src/auditor/batch.sh b/src/auditor/batch.sh
index ec9ed8309..1f8896c42 100755
--- a/src/auditor/batch.sh
+++ b/src/auditor/batch.sh
@@ -193,7 +193,7 @@ echo " DONE"
echo -n "Setting up merchant"
-curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://x-taler-bank/localhost/43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
+curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
echo " DONE"
diff --git a/src/auditor/generate-auditor-basedb.sh b/src/auditor/generate-auditor-basedb.sh
index 2ab403a83..95fc2216c 100755
--- a/src/auditor/generate-auditor-basedb.sh
+++ b/src/auditor/generate-auditor-basedb.sh
@@ -398,7 +398,7 @@ echo " DONE"
echo -n "Setting up merchant"
-curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
+curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
echo " DONE"
diff --git a/src/auditor/generate-revoke-basedb.sh b/src/auditor/generate-revoke-basedb.sh
index 88d8e562c..745b96b72 100755
--- a/src/auditor/generate-revoke-basedb.sh
+++ b/src/auditor/generate-revoke-basedb.sh
@@ -400,7 +400,7 @@ echo " DONE"
# Setup merchant
echo -n "Setting up merchant"
-curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "payto_uris":["payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
+curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
# run wallet CLI
diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c
index a7ccf800a..2b4bf8558 100644
--- a/src/auditor/taler-auditor-sync.c
+++ b/src/auditor/taler-auditor-sync.c
@@ -605,6 +605,9 @@ main (int argc,
level,
NULL));
GNUNET_free (level);
+ /* suppress compiler warnings... */
+ GNUNET_assert (NULL != src_cfgfile);
+ GNUNET_assert (NULL != dst_cfgfile);
if (0 == strcmp (src_cfgfile,
dst_cfgfile))
{
diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c
index 7637e463d..8edbcf29b 100644
--- a/src/auditor/taler-helper-auditor-coins.c
+++ b/src/auditor/taler-helper-auditor-coins.c
@@ -172,9 +172,9 @@ coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
{
uint32_t i;
- memcpy (&i,
- coin_pub,
- sizeof (i));
+ GNUNET_memcpy (&i,
+ coin_pub,
+ sizeof (i));
return i % MAX_COIN_HISTORIES;
}
diff --git a/src/auditor/taler-helper-auditor-wire.c b/src/auditor/taler-helper-auditor-wire.c
index e3e3764a7..bfc465b05 100644
--- a/src/auditor/taler-helper-auditor-wire.c
+++ b/src/auditor/taler-helper-auditor-wire.c
@@ -674,12 +674,12 @@ hash_rc (const char *receiver_account,
size_t slen = strlen (receiver_account);
char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
- memcpy (buf,
- wtid,
- sizeof (*wtid));
- memcpy (&buf[sizeof (*wtid)],
- receiver_account,
- slen);
+ GNUNET_memcpy (buf,
+ wtid,
+ sizeof (*wtid));
+ GNUNET_memcpy (&buf[sizeof (*wtid)],
+ receiver_account,
+ slen);
GNUNET_CRYPTO_hash (buf,
sizeof (buf),
key);
@@ -1483,10 +1483,10 @@ history_debit_cb (void *cls,
switch (dhr->http_status)
{
case MHD_HTTP_OK:
- for (unsigned int i = 0; i<dhr->details.success.details_length; i++)
+ for (unsigned int i = 0; i<dhr->details.ok.details_length; i++)
{
const struct TALER_BANK_DebitDetails *dd
- = &dhr->details.success.details[i];
+ = &dhr->details.ok.details[i];
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Analyzing bank DEBIT at %s of %s with WTID %s\n",
GNUNET_TIME_timestamp2s (dd->execution_date),
@@ -1504,9 +1504,9 @@ history_debit_cb (void *cls,
roi->details.execution_date = dd->execution_date;
roi->details.wtid = dd->wtid;
roi->details.credit_account_uri = (const char *) &roi[1];
- memcpy (&roi[1],
- dd->credit_account_uri,
- slen);
+ GNUNET_memcpy (&roi[1],
+ dd->credit_account_uri,
+ slen);
if (GNUNET_OK !=
GNUNET_CONTAINER_multihashmap_put (out_map,
&roi->subject_hash,
@@ -1678,9 +1678,9 @@ reserve_in_cb (void *cls,
rii->details.execution_date = execution_date;
rii->details.reserve_pub = *reserve_pub;
rii->details.debit_account_uri = (const char *) &rii[1];
- memcpy (&rii[1],
- sender_account_details,
- slen);
+ GNUNET_memcpy (&rii[1],
+ sender_account_details,
+ slen);
GNUNET_CRYPTO_hash (&wire_reference,
sizeof (uint64_t),
&rii->row_off_hash);
@@ -1978,10 +1978,10 @@ history_credit_cb (void *cls,
switch (chr->http_status)
{
case MHD_HTTP_OK:
- for (unsigned int i = 0; i<chr->details.success.details_length; i++)
+ for (unsigned int i = 0; i<chr->details.ok.details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd
- = &chr->details.success.details[i];
+ = &chr->details.ok.details[i];
if (! analyze_credit (wa,
cd))
diff --git a/src/bank-lib/bank_api_credit.c b/src/bank-lib/bank_api_credit.c
index 4842de969..14ddcd729 100644
--- a/src/bank-lib/bank_api_credit.c
+++ b/src/bank-lib/bank_api_credit.c
@@ -131,8 +131,8 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
return GNUNET_SYSERR;
}
}
- chr.details.success.details_length = len;
- chr.details.success.details = cd;
+ chr.details.ok.details_length = len;
+ chr.details.ok.details = cd;
hh->hcb (hh->hcb_cls,
&chr);
}
diff --git a/src/bank-lib/bank_api_debit.c b/src/bank-lib/bank_api_debit.c
index bd9b11941..e40156ce7 100644
--- a/src/bank-lib/bank_api_debit.c
+++ b/src/bank-lib/bank_api_debit.c
@@ -133,8 +133,8 @@ parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
return GNUNET_SYSERR;
}
}
- dhr.details.success.details_length = len;
- dhr.details.success.details = dd;
+ dhr.details.ok.details_length = len;
+ dhr.details.ok.details = dd;
hh->hcb (hh->hcb_cls,
&dhr);
}
diff --git a/src/bank-lib/bank_api_transfer.c b/src/bank-lib/bank_api_transfer.c
index 3b50018d3..94d8c6b61 100644
--- a/src/bank-lib/bank_api_transfer.c
+++ b/src/bank-lib/bank_api_transfer.c
@@ -99,12 +99,12 @@ TALER_BANK_prepare_transfer (
wp->account_len = htonl ((uint32_t) d_len);
wp->exchange_url_len = htonl ((uint32_t) u_len);
end = (char *) &wp[1];
- memcpy (end,
- destination_account_payto_uri,
- d_len);
- memcpy (end + d_len,
- exchange_base_url,
- u_len);
+ GNUNET_memcpy (end,
+ destination_account_payto_uri,
+ d_len);
+ GNUNET_memcpy (end + d_len,
+ exchange_base_url,
+ u_len);
*buf = (char *) wp;
}
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index 60492e50f..c916ad70c 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -1389,9 +1389,9 @@ make_transfer (
if (NULL != timestamp)
*timestamp = t->date;
t->type = T_DEBIT;
- memcpy (t->subject.debit.exchange_base_url,
- exchange_base_url,
- url_len);
+ GNUNET_memcpy (t->subject.debit.exchange_base_url,
+ exchange_base_url,
+ url_len);
t->subject.debit.wtid = *subject;
if (NULL == request_uid)
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
diff --git a/src/bank-lib/taler-exchange-wire-gateway-client.c b/src/bank-lib/taler-exchange-wire-gateway-client.c
index 5bfd9311d..a972bcfd2 100644
--- a/src/bank-lib/taler-exchange-wire-gateway-client.c
+++ b/src/bank-lib/taler-exchange-wire-gateway-client.c
@@ -179,10 +179,10 @@ credit_history_cb (void *cls,
global_ret = 0;
break;
case MHD_HTTP_OK:
- for (unsigned int i = 0; i<reply->details.success.details_length; i++)
+ for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd =
- &reply->details.success.details[i];
+ &reply->details.ok.details[i];
/* If credit/debit accounts were specified, use as a filter */
if ( (NULL != credit_account) &&
@@ -279,10 +279,10 @@ debit_history_cb (void *cls,
global_ret = 0;
break;
case MHD_HTTP_OK:
- for (unsigned int i = 0; i<reply->details.success.details_length; i++)
+ for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
{
const struct TALER_BANK_DebitDetails *dd =
- &reply->details.success.details[i];
+ &reply->details.ok.details[i];
/* If credit/debit accounts were specified, use as a filter */
if ( (NULL != credit_account) &&
diff --git a/src/exchange-tools/taler-auditor-offline.c b/src/exchange-tools/taler-auditor-offline.c
index 53135d9fa..f239c11a9 100644
--- a/src/exchange-tools/taler-auditor-offline.c
+++ b/src/exchange-tools/taler-auditor-offline.c
@@ -644,26 +644,19 @@ do_upload (char *const *args)
* a particular exchange and what keys the exchange is using.
*
* @param cls closure with the `char **` remaining args
- * @param hr HTTP response data
- * @param keys information about the various keys used
- * by the exchange, NULL if /keys failed
- * @param compat protocol compatibility information
+ * @param kr response data
*/
static void
keys_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_Keys *keys,
- enum TALER_EXCHANGE_VersionCompatibility compat)
+ const struct TALER_EXCHANGE_KeysResponse *kr)
{
char *const *args = cls;
- (void) keys;
- (void) compat;
- switch (hr->http_status)
+ switch (kr->hr.http_status)
{
case MHD_HTTP_OK:
- if (! json_is_object (hr->reply))
+ if (! json_is_object (kr->hr.reply))
{
GNUNET_break (0);
TALER_EXCHANGE_disconnect (exchange);
@@ -676,9 +669,9 @@ keys_cb (
default:
fprintf (stderr,
"Failed to download keys: %s (HTTP status: %u/%u)\n",
- hr->hint,
- hr->http_status,
- (unsigned int) hr->ec);
+ kr->hr.hint,
+ kr->hr.http_status,
+ (unsigned int) kr->hr.ec);
TALER_EXCHANGE_disconnect (exchange);
exchange = NULL;
test_shutdown ();
@@ -689,7 +682,7 @@ keys_cb (
GNUNET_JSON_pack_string ("operation",
OP_INPUT_KEYS),
GNUNET_JSON_pack_object_incref ("arguments",
- (json_t *) hr->reply));
+ (json_t *) kr->hr.reply));
if (NULL == args[0])
{
json_dumpf (in,
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c
index 660864b77..80765dd9b 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -24,6 +24,8 @@
#include "taler_json_lib.h"
#include "taler_exchange_service.h"
#include "taler_extensions.h"
+#include <regex.h>
+
/**
* Name of the input for the 'sign' and 'show' operation.
@@ -1119,14 +1121,15 @@ load_offline_key (int do_create)
* Function called with information about the post revocation operation result.
*
* @param cls closure with a `struct DenomRevocationRequest`
- * @param hr HTTP response data
+ * @param dr response data
*/
static void
denom_revocation_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *dr)
{
struct DenomRevocationRequest *drr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@@ -1208,14 +1211,15 @@ upload_denom_revocation (const char *exchange_url,
* Function called with information about the post revocation operation result.
*
* @param cls closure with a `struct SignkeyRevocationRequest`
- * @param hr HTTP response data
+ * @param sr response data
*/
static void
signkey_revocation_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *sr)
{
struct SignkeyRevocationRequest *srr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@@ -1489,13 +1493,14 @@ upload_auditor_del (const char *exchange_url,
* Function called with information about the post wire add operation result.
*
* @param cls closure with a `struct WireAddRequest`
- * @param hr HTTP response data
+ * @param wer response data
*/
static void
wire_add_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer)
{
struct WireAddRequest *war = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@@ -1533,10 +1538,21 @@ upload_wire_add (const char *exchange_url,
struct GNUNET_TIME_Timestamp start_time;
struct WireAddRequest *war;
const char *err_name;
+ const char *conversion_url = NULL;
+ json_t *debit_restrictions;
+ json_t *credit_restrictions;
unsigned int err_line;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("payto_uri",
&payto_uri),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("conversion_url",
+ &conversion_url),
+ NULL),
+ GNUNET_JSON_spec_json ("debit_restrictions",
+ &debit_restrictions),
+ GNUNET_JSON_spec_json ("credit_restrictions",
+ &credit_restrictions),
GNUNET_JSON_spec_timestamp ("validity_start",
&start_time),
GNUNET_JSON_spec_fixed_auto ("master_sig_add",
@@ -1561,6 +1577,7 @@ upload_wire_add (const char *exchange_url,
stderr,
JSON_INDENT (2));
global_ret = EXIT_FAILURE;
+ GNUNET_JSON_parse_free (spec);
test_shutdown ();
return;
}
@@ -1574,6 +1591,7 @@ upload_wire_add (const char *exchange_url,
"payto:// URI `%s' is malformed\n",
payto_uri);
global_ret = EXIT_FAILURE;
+ GNUNET_JSON_parse_free (spec);
test_shutdown ();
return;
}
@@ -1588,6 +1606,7 @@ upload_wire_add (const char *exchange_url,
"payto URI is malformed: %s\n",
msg);
GNUNET_free (msg);
+ GNUNET_JSON_parse_free (spec);
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
@@ -1599,6 +1618,9 @@ upload_wire_add (const char *exchange_url,
TALER_EXCHANGE_management_enable_wire (ctx,
exchange_url,
payto_uri,
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
start_time,
&master_sig_add,
&master_sig_wire,
@@ -1607,6 +1629,7 @@ upload_wire_add (const char *exchange_url,
GNUNET_CONTAINER_DLL_insert (war_head,
war_tail,
war);
+ GNUNET_JSON_parse_free (spec);
}
@@ -1614,13 +1637,14 @@ upload_wire_add (const char *exchange_url,
* Function called with information about the post wire del operation result.
*
* @param cls closure with a `struct WireDelRequest`
- * @param hr HTTP response data
+ * @param wdres response data
*/
static void
wire_del_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdres)
{
struct WireDelRequest *wdr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@@ -1927,14 +1951,15 @@ upload_global_fee (const char *exchange_url,
* Function called with information about the drain profits operation.
*
* @param cls closure with a `struct DrainProfitsRequest`
- * @param hr HTTP response data
+ * @param mdr response data
*/
static void
drain_profits_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementDrainResponse *mdr)
{
struct DrainProfitsRequest *dpr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@@ -2033,14 +2058,15 @@ upload_drain (const char *exchange_url,
* Function called with information about the post upload keys operation result.
*
* @param cls closure with a `struct UploadKeysRequest`
- * @param hr HTTP response data
+ * @param mr response data
*/
static void
keys_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr)
{
struct UploadKeysRequest *ukr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@@ -2206,14 +2232,15 @@ upload_keys (const char *exchange_url,
* Function called with information about the post upload extensions operation result.
*
* @param cls closure with a `struct UploadExtensionsRequest`
- * @param hr HTTP response data
+ * @param er response data
*/
static void
extensions_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *er)
{
struct UploadExtensionsRequest *uer = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@@ -2447,14 +2474,15 @@ add_partner (const char *exchange_url,
* Function called with information about the AML officer update operation.
*
* @param cls closure with a `struct AmlStaffRequest`
- * @param hr HTTP response data
+ * @param ar response data
*/
static void
update_aml_officer_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar)
{
struct AmlStaffRequest *asr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@@ -2950,6 +2978,96 @@ do_del_auditor (char *const *args)
/**
+ * Parse account restriction.
+ *
+ * @param args the array of command-line arguments to process next
+ * @param[in,out] restrictions JSON array to update
+ * @return -1 on error, otherwise number of arguments from @a args that were used
+ */
+static int
+parse_restriction (char *const *args,
+ json_t *restrictions)
+{
+ if (NULL == args[0])
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Restriction TYPE argument missing\n");
+ return -1;
+ }
+ if (0 == strcmp (args[0],
+ "deny"))
+ {
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ restrictions,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "deny"))));
+ return 1;
+ }
+ if (0 == strcmp (args[0],
+ "regex"))
+ {
+ json_t *i18n;
+ json_error_t err;
+
+ if ( (NULL == args[1]) ||
+ (NULL == args[2]) ||
+ (NULL == args[3]) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n");
+ return -1;
+ }
+ {
+ regex_t ex;
+
+ if (0 != regcomp (&ex,
+ args[1],
+ REG_NOSUB | REG_EXTENDED))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid regular expression `%s'\n",
+ args[1]);
+ return -1;
+ }
+ regfree (&ex);
+ }
+
+ i18n = json_loads (args[3],
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == i18n)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid JSON for restriction of type `regex': `%s` at %d\n",
+ args[3],
+ err.position);
+ return -1;
+ }
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ restrictions,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "regex"),
+ GNUNET_JSON_pack_string ("regex",
+ args[1]),
+ GNUNET_JSON_pack_string ("human_hint",
+ args[2]),
+ GNUNET_JSON_pack_object_steal ("human_hint_i18n",
+ i18n)
+ )));
+ return 4;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Restriction TYPE `%s' unsupported\n",
+ args[0]);
+ return -1;
+}
+
+
+/**
* Add wire account.
*
* @param args the array of command-line arguments to process next;
@@ -2961,6 +3079,10 @@ do_add_wire (char *const *args)
struct TALER_MasterSignatureP master_sig_add;
struct TALER_MasterSignatureP master_sig_wire;
struct GNUNET_TIME_Timestamp now;
+ const char *conversion_url = NULL;
+ json_t *debit_restrictions;
+ json_t *credit_restrictions;
+ unsigned int num_args = 1;
if (NULL != in)
{
@@ -3011,24 +3133,101 @@ do_add_wire (char *const *args)
}
GNUNET_free (wire_method);
}
+ debit_restrictions = json_array ();
+ GNUNET_assert (NULL != debit_restrictions);
+ credit_restrictions = json_array ();
+ GNUNET_assert (NULL != credit_restrictions);
+ while (NULL != args[num_args])
+ {
+ if (0 == strcmp (args[num_args],
+ "conversion-url"))
+ {
+ num_args++;
+ conversion_url = args[num_args];
+ if (NULL == conversion_url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "'conversion-url' requires an argument\n");
+ global_ret = EXIT_INVALIDARGUMENT;
+ test_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ num_args++;
+ continue;
+ }
+ if (0 == strcmp (args[num_args],
+ "credit-restriction"))
+ {
+ int iret;
+
+ num_args++;
+ iret = parse_restriction (&args[num_args],
+ credit_restrictions);
+ if (iret <= 0)
+ {
+ global_ret = EXIT_INVALIDARGUMENT;
+ test_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ num_args += iret;
+ continue;
+ }
+ if (0 == strcmp (args[num_args],
+ "debit-restriction"))
+ {
+ int iret;
+
+ num_args++;
+ iret = parse_restriction (&args[num_args],
+ debit_restrictions);
+ if (iret <= 0)
+ {
+ global_ret = EXIT_INVALIDARGUMENT;
+ test_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ num_args += iret;
+ continue;
+ }
+ break;
+ }
TALER_exchange_offline_wire_add_sign (args[0],
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
now,
&master_priv,
&master_sig_add);
TALER_exchange_wire_signature_make (args[0],
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
&master_priv,
&master_sig_wire);
output_operation (OP_ENABLE_WIRE,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("payto_uri",
args[0]),
+ GNUNET_JSON_pack_array_steal ("debit_restrictions",
+ debit_restrictions),
+ GNUNET_JSON_pack_array_steal ("credit_restrictions",
+ credit_restrictions),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("conversion_url",
+ conversion_url)),
GNUNET_JSON_pack_timestamp ("validity_start",
now),
GNUNET_JSON_pack_data_auto ("master_sig_add",
&master_sig_add),
GNUNET_JSON_pack_data_auto ("master_sig_wire",
&master_sig_wire)));
- next (args + 1);
+ next (args + num_args);
}
@@ -3643,18 +3842,15 @@ enable_aml_staff (char *const *args)
* whether there are subsequent commands).
*
* @param cls closure with the `char **` remaining args
- * @param hr HTTP response data
- * @param keys information about the various keys used
- * by the exchange, NULL if /management/keys failed
+ * @param mgr response data
*/
static void
download_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_FutureKeys *keys)
+ const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr)
{
char *const *args = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
- (void) keys;
mgkh = NULL;
switch (hr->http_status)
{
@@ -5045,7 +5241,7 @@ work (void *cls)
{
.name = "enable-account",
.help =
- "enable wire account of the exchange (payto-URI must be given as argument)",
+ "enable wire account of the exchange (payto-URI must be given as argument; for optional argument see man page)",
.cb = &do_add_wire
},
{
diff --git a/src/exchange/taler-exchange-closer.c b/src/exchange/taler-exchange-closer.c
index 41c6436a1..63a98bd0d 100644
--- a/src/exchange/taler-exchange-closer.c
+++ b/src/exchange/taler-exchange-closer.c
@@ -312,10 +312,10 @@ expired_reserve_cb (void *cls,
memset (&wtid,
0,
sizeof (wtid));
- memcpy (&wtid,
- reserve_pub,
- GNUNET_MIN (sizeof (wtid),
- sizeof (*reserve_pub)));
+ GNUNET_memcpy (&wtid,
+ reserve_pub,
+ GNUNET_MIN (sizeof (wtid),
+ sizeof (*reserve_pub)));
qs = db_plugin->insert_reserve_closed (db_plugin->cls,
reserve_pub,
now,
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index d247d981b..7e11655c1 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -542,7 +542,6 @@ handle_get_aml (struct TEH_RequestContext *rc,
TALER_EC_GENERIC_DB_FETCH_FAILED,
NULL);
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
@@ -932,9 +931,9 @@ proceed_with_handler (struct TEH_RequestContext *rc,
/* Parse command-line arguments */
/* make a copy of 'url' because 'strtok_r()' will modify */
- memcpy (d,
- url,
- ulen);
+ GNUNET_memcpy (d,
+ url,
+ ulen);
i = 0;
args[i++] = strtok_r (d, "/", &sp);
while ( (NULL != args[i - 1]) &&
@@ -1617,33 +1616,8 @@ handle_mhd_request (void *cls,
if (0 == strcasecmp (method,
MHD_HTTP_METHOD_POST))
{
- const char *cl;
-
- /* Maybe check for maximum upload size
- and refuse requests if they are just too big. */
- cl = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONTENT_LENGTH);
- if (NULL != cl)
- {
- unsigned long long cv;
- char dummy;
-
- if (1 != sscanf (cl,
- "%llu%c",
- &cv,
- &dummy))
- {
- /* Not valid HTTP request, just close connection. */
- GNUNET_break_op (0);
- return MHD_NO;
- }
- if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_request_too_large (connection);
- }
- }
+ TALER_MHD_check_content_length (connection,
+ TALER_MHD_REQUEST_BUFFER_MAX);
}
}
@@ -2215,6 +2189,7 @@ do_shutdown (void *cls)
mhd = TALER_MHD_daemon_stop ();
TEH_resume_keys_requests (true);
+ TEH_deposits_get_cleanup ();
TEH_reserves_get_cleanup ();
TEH_purses_get_cleanup ();
TEH_kyc_check_cleanup ();
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
index 5cee7277a..776c0d8d9 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
@@ -22,6 +22,8 @@
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include <microhttpd.h>
+#include "taler-exchange-httpd_metrics.h"
+#include "taler_exchangedb_plugin.h"
#include "taler_mhd_lib.h"
#include "taler-exchange-httpd_mhd.h"
#include "taler-exchange-httpd_age-withdraw_reveal.h"
@@ -387,12 +389,10 @@ denomination_is_valid (
struct TEH_DenominationKey *dks,
MHD_RESULT *result)
{
- dks = TEH_keys_denomination_by_hash2 (
- ksh,
- denom_h,
- connection,
- result);
-
+ dks = TEH_keys_denomination_by_hash2 (ksh,
+ denom_h,
+ connection,
+ result);
if (NULL == dks)
{
/* The denomination doesn't exist */
@@ -635,7 +635,7 @@ verify_commitment_and_max_age (
}
else
{
- /* FIXME:oec: Refactor this block out into its own function */
+ /* FIXME[oec] Refactor this block out into its own function */
size_t j = (TALER_CNC_KAPPA - 1) * c + k; /* Index into disclosed_coin_secrets[] */
const struct TALER_PlanchetMasterSecretP *secret;
@@ -785,6 +785,43 @@ verify_commitment_and_max_age (
/**
+ * @brief Send a response for "/age-withdraw/$RCH/reveal"
+ *
+ * @param connection The http connection to the client to send the response to
+ * @param num_coins Number of new coins with age restriction for which we reveal data
+ * @param awrcs array of @a num_coins signatures revealed
+ * @return a MHD result code
+ */
+static MHD_RESULT
+reply_age_withdraw_reveal_success (
+ struct MHD_Connection *connection,
+ unsigned int num_coins,
+ const struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin *awrcs)
+{
+ json_t *list = json_array ();
+ GNUNET_assert (NULL != list);
+
+ for (unsigned int index = 0;
+ index < num_coins;
+ index++)
+ {
+ json_t *obj = GNUNET_JSON_PACK (
+ TALER_JSON_pack_blinded_denom_sig ("ev_sig",
+ &awrcs[index].coin_sig));
+ GNUNET_assert (0 ==
+ json_array_append_new (list,
+ obj));
+ }
+
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_array_steal ("ev_sigs",
+ list));
+}
+
+
+/**
* @brief Signs and persists the undisclosed coins
*
* @param connection HTTP-connection to the client
@@ -796,7 +833,7 @@ verify_commitment_and_max_age (
* @return GNUNET_OK on success, GNUNET_SYSERR otherwise
*/
static enum GNUNET_GenericReturnValue
-finalize_age_withdraw_and_sign (
+sign_and_finalize_age_withdraw (
struct MHD_Connection *connection,
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
const uint32_t num_coins,
@@ -806,7 +843,9 @@ finalize_age_withdraw_and_sign (
{
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
struct TEH_CoinSignData csds[num_coins];
- struct TALER_BlindedDenominationSignature bss[num_coins];
+ struct TALER_BlindedDenominationSignature bds[num_coins];
+ struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin awrcs[num_coins];
+ enum GNUNET_DB_QueryStatus qs;
for (uint32_t i = 0; i<num_coins; i++)
{
@@ -814,13 +853,13 @@ finalize_age_withdraw_and_sign (
csds[i].bp = &coin_evs[i];
}
- /* First, sign the the blinded coins */
+ /* Sign the the blinded coins first */
{
enum TALER_ErrorCode ec;
ec = TEH_keys_denomination_batch_sign (csds,
num_coins,
false,
- bss);
+ bds);
if (TALER_EC_NONE != ec)
{
GNUNET_break (0);
@@ -831,12 +870,107 @@ finalize_age_withdraw_and_sign (
}
}
- /* TODO[oec]:
- * - in a transaction: save the coins.
- * - add signature response
- */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Signatures ready, starting DB interaction\n");
+
+ /* Prepare the data for insertion */
+ for (uint32_t i = 0; i<num_coins; i++)
+ {
+ TALER_coin_ev_hash (&coin_evs[i],
+ csds[i].h_denom_pub,
+ &awrcs[i].h_coin_ev);
+ awrcs[i].h_denom_pub = *csds[i].h_denom_pub;
+ awrcs[i].coin_sig = bds[i];
+ }
+
+ /* Persist operation result in DB, transactionally */
+ for (unsigned int r = 0; r < MAX_TRANSACTION_COMMIT_RETRIES; r++)
+ {
+ bool changed = false;
+
+ /* Transaction start */
+ if (GNUNET_OK !=
+ TEH_plugin->start (TEH_plugin->cls,
+ "insert_age_withdraw_reveal batch"))
+ {
+ GNUNET_break (0);
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_START_FAILED,
+ NULL);
+ goto cleanup;
+ }
+
+ qs = TEH_plugin->insert_age_withdraw_reveal (TEH_plugin->cls,
+ h_commitment,
+ num_coins,
+ awrcs);
+
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ TEH_plugin->rollback (TEH_plugin->cls);
+ continue;
+ }
+ else if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ TEH_plugin->rollback (TEH_plugin->cls);
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_age_withdraw_reveal");
+ goto cleanup;
+ }
+
+ changed = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+
+ /* Commit the transaction */
+ qs = TEH_plugin->commit (TEH_plugin->cls);
+ if (qs >= 0)
+ {
+ if (changed)
+ TEH_METRICS_num_success[TEH_MT_SUCCESS_AGE_WITHDRAW_REVEAL]++;
+
+ break; /* success */
+
+ }
+ else if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ TEH_plugin->rollback (TEH_plugin->cls);
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_COMMIT_FAILED,
+ NULL);
+ goto cleanup;
+ }
+ else
+ {
+ TEH_plugin->rollback (TEH_plugin->cls);
+ }
+ } /* end of retry */
+
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ GNUNET_break (0);
+ TEH_plugin->rollback (TEH_plugin->cls);
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_SOFT_FAILURE,
+ NULL);
+ goto cleanup;
+ }
+
+ /* Generate final (positive) response */
+ ret = reply_age_withdraw_reveal_success (connection,
+ num_coins,
+ awrcs);
+cleanup:
+ GNUNET_break (GNUNET_OK != ret);
-#pragma message "FIXME[oec]: implement finalize_age_withdraw_and_sign"
+ /* Free resources */
+ for (unsigned int i = 0; i<num_coins; i++)
+ TALER_blinded_denom_sig_free (&awrcs[i].coin_sig);
return ret;
}
@@ -922,7 +1056,7 @@ TEH_handler_age_withdraw_reveal (
break;
/* Finally, sign and persist the coins */
- if (GNUNET_OK != finalize_age_withdraw_and_sign (
+ if (GNUNET_OK != sign_and_finalize_age_withdraw (
rc->connection,
&actx.commitment.h_commitment,
actx.num_coins,
diff --git a/src/exchange/taler-exchange-httpd_config.h b/src/exchange/taler-exchange-httpd_config.h
index 7763cdeb5..95380e0a8 100644
--- a/src/exchange/taler-exchange-httpd_config.h
+++ b/src/exchange/taler-exchange-httpd_config.h
@@ -41,7 +41,7 @@
*
* Returned via both /config and /keys endpoints.
*/
-#define EXCHANGE_PROTOCOL_VERSION "14:0:2"
+#define EXCHANGE_PROTOCOL_VERSION "15:0:0"
/**
diff --git a/src/exchange/taler-exchange-httpd_deposits_get.c b/src/exchange/taler-exchange-httpd_deposits_get.c
index ebbb13e08..818900c60 100644
--- a/src/exchange/taler-exchange-httpd_deposits_get.c
+++ b/src/exchange/taler-exchange-httpd_deposits_get.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2017, 2021 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
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
@@ -23,6 +23,7 @@
#include <jansson.h>
#include <microhttpd.h>
#include <pthread.h>
+#include "taler_dbevents.h"
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
#include "taler_signatures.h"
@@ -38,6 +39,26 @@ struct DepositWtidContext
{
/**
+ * Kept in a DLL.
+ */
+ struct DepositWtidContext *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DepositWtidContext *prev;
+
+ /**
+ * Context for the request we are processing.
+ */
+ struct TEH_RequestContext *rc;
+
+ /**
+ * Subscription for the database event we are waiting for.
+ */
+ struct GNUNET_DB_EventHandler *eh;
+
+ /**
* Hash over the proposal data of the contract for which this deposit is made.
*/
struct TALER_PrivateContractHashP h_contract_terms;
@@ -65,6 +86,12 @@ struct DepositWtidContext
struct TALER_WireTransferIdentifierRawP wtid;
/**
+ * Signature by the merchant.
+ */
+ struct TALER_MerchantSignatureP merchant_sig;
+
+
+ /**
* Set by #handle_wtid data to the coin's contribution to the wire transfer.
*/
struct TALER_Amount coin_contribution;
@@ -80,6 +107,11 @@ struct DepositWtidContext
struct GNUNET_TIME_Timestamp execution_time;
/**
+ * Timeout of the request, for long-polling.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
* Set by #handle_wtid to the coin contribution to the transaction
* (that is, @e coin_contribution minus @e coin_fee).
*/
@@ -101,10 +133,46 @@ struct DepositWtidContext
* Set to #GNUNET_SYSERR if there was a serious error.
*/
enum GNUNET_GenericReturnValue pending;
+
+ /**
+ * #GNUNET_YES if we were suspended, #GNUNET_SYSERR
+ * if we were woken up due to shutdown.
+ */
+ enum GNUNET_GenericReturnValue suspended;
};
/**
+ * Head of DLL of suspended requests.
+ */
+static struct DepositWtidContext *dwc_head;
+
+/**
+ * Tail of DLL of suspended requests.
+ */
+static struct DepositWtidContext *dwc_tail;
+
+
+void
+TEH_deposits_get_cleanup ()
+{
+ struct DepositWtidContext *n;
+ for (struct DepositWtidContext *ctx = dwc_head;
+ NULL != ctx;
+ ctx = n)
+ {
+ n = ctx->next;
+ GNUNET_assert (GNUNET_YES == ctx->suspended);
+ ctx->suspended = GNUNET_SYSERR;
+ MHD_resume_connection (ctx->rc->connection);
+ GNUNET_CONTAINER_DLL_remove (dwc_head,
+ dwc_tail,
+ ctx);
+ }
+}
+
+
+/**
* A merchant asked for details about a deposit. Provide
* them. Generates the 200 reply.
*
@@ -228,33 +296,96 @@ deposits_get_transaction (void *cls,
/**
+ * Function called on events received from Postgres.
+ * Wakes up long pollers.
+ *
+ * @param cls the `struct DepositWtidContext *`
+ * @param extra additional event data provided
+ * @param extra_size number of bytes in @a extra
+ */
+static void
+db_event_cb (void *cls,
+ const void *extra,
+ size_t extra_size)
+{
+ struct DepositWtidContext *ctx = cls;
+ struct GNUNET_AsyncScopeSave old_scope;
+
+ (void) extra;
+ (void) extra_size;
+ if (GNUNET_NO != ctx->suspended)
+ return; /* might get multiple wake-up events */
+ GNUNET_CONTAINER_DLL_remove (dwc_head,
+ dwc_tail,
+ ctx);
+ GNUNET_async_scope_enter (&ctx->rc->async_scope_id,
+ &old_scope);
+ TEH_check_invariants ();
+ ctx->suspended = GNUNET_NO;
+ MHD_resume_connection (ctx->rc->connection);
+ TALER_MHD_daemon_trigger ();
+ TEH_check_invariants ();
+ GNUNET_async_scope_restore (&old_scope);
+}
+
+
+/**
* Lookup and return the wire transfer identifier.
*
- * @param connection the MHD connection to handle
* @param ctx context of the signed request to execute
* @return MHD result code
*/
static MHD_RESULT
handle_track_transaction_request (
- struct MHD_Connection *connection,
struct DepositWtidContext *ctx)
{
- MHD_RESULT mhd_ret;
-
- if (GNUNET_OK !=
- TEH_DB_run_transaction (connection,
- "handle deposits GET",
- TEH_MT_REQUEST_OTHER,
- &mhd_ret,
- &deposits_get_transaction,
- ctx))
- return mhd_ret;
+ struct MHD_Connection *connection = ctx->rc->connection;
+
+ if ( (GNUNET_TIME_absolute_is_future (ctx->timeout)) &&
+ (NULL == ctx->eh) )
+ {
+ struct TALER_CoinDepositEventP rep = {
+ .header.size = htons (sizeof (rep)),
+ .header.type = htons (TALER_DBEVENT_EXCHANGE_DEPOSIT_STATUS_CHANGED),
+ .merchant_pub = ctx->merchant
+ };
+
+ ctx->eh = TEH_plugin->event_listen (
+ TEH_plugin->cls,
+ GNUNET_TIME_absolute_get_remaining (ctx->timeout),
+ &rep.header,
+ &db_event_cb,
+ ctx);
+ }
+ {
+ MHD_RESULT mhd_ret;
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ "handle deposits GET",
+ TEH_MT_REQUEST_OTHER,
+ &mhd_ret,
+ &deposits_get_transaction,
+ ctx))
+ return mhd_ret;
+ }
if (GNUNET_SYSERR == ctx->pending)
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
"wire fees exceed aggregate in database");
- if (ctx->pending)
+ if (GNUNET_YES == ctx->pending)
+ {
+ if ( (GNUNET_TIME_absolute_is_future (ctx->timeout)) &&
+ (GNUNET_NO == ctx->suspended) )
+ {
+ GNUNET_CONTAINER_DLL_insert (dwc_head,
+ dwc_tail,
+ ctx);
+ ctx->suspended = GNUNET_YES;
+ MHD_suspend_connection (connection);
+ return MHD_YES;
+ }
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_ACCEPTED,
@@ -270,94 +401,118 @@ handle_track_transaction_request (
ctx->kyc.ok),
GNUNET_JSON_pack_timestamp ("execution_time",
ctx->execution_time));
+ }
return reply_deposit_details (connection,
ctx);
}
+/**
+ * Function called to clean up a context.
+ *
+ * @param rc request context with data to clean up
+ */
+static void
+dwc_cleaner (struct TEH_RequestContext *rc)
+{
+ struct DepositWtidContext *ctx = rc->rh_ctx;
+
+ GNUNET_assert (GNUNET_NO == ctx->suspended);
+ if (NULL != ctx->eh)
+ {
+ TEH_plugin->event_listen_cancel (TEH_plugin->cls,
+ ctx->eh);
+ ctx->eh = NULL;
+ }
+ GNUNET_free (ctx);
+}
+
+
MHD_RESULT
TEH_handler_deposits_get (struct TEH_RequestContext *rc,
const char *const args[4])
{
- enum GNUNET_GenericReturnValue res;
- struct TALER_MerchantSignatureP merchant_sig;
- struct DepositWtidContext ctx;
+ struct DepositWtidContext *ctx = rc->rh_ctx;
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (args[0],
- strlen (args[0]),
- &ctx.h_wire,
- sizeof (ctx.h_wire)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_WIRE,
- args[0]);
- }
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (args[1],
- strlen (args[1]),
- &ctx.merchant,
- sizeof (ctx.merchant)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_MERCHANT_PUB,
- args[1]);
- }
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (args[2],
- strlen (args[2]),
- &ctx.h_contract_terms,
- sizeof (ctx.h_contract_terms)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_CONTRACT_TERMS,
- args[2]);
- }
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (args[3],
- strlen (args[3]),
- &ctx.coin_pub,
- sizeof (ctx.coin_pub)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_COIN_PUB,
- args[3]);
- }
- res = TALER_MHD_parse_request_arg_data (rc->connection,
- "merchant_sig",
- &merchant_sig,
- sizeof (merchant_sig));
- if (GNUNET_SYSERR == res)
- return MHD_NO; /* internal error */
- if (GNUNET_NO == res)
- return MHD_YES; /* parse error */
- TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
+ if (NULL == ctx)
{
+ ctx = GNUNET_new (struct DepositWtidContext);
+ ctx->rc = rc;
+ rc->rh_ctx = ctx;
+ rc->rh_cleaner = &dwc_cleaner;
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &ctx->h_wire,
+ sizeof (ctx->h_wire)))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_WIRE,
+ args[0]);
+ }
if (GNUNET_OK !=
- TALER_merchant_deposit_verify (&ctx.merchant,
- &ctx.coin_pub,
- &ctx.h_contract_terms,
- &ctx.h_wire,
- &merchant_sig))
+ GNUNET_STRINGS_string_to_data (args[1],
+ strlen (args[1]),
+ &ctx->merchant,
+ sizeof (ctx->merchant)))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_DEPOSITS_GET_MERCHANT_SIGNATURE_INVALID,
- NULL);
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_MERCHANT_PUB,
+ args[1]);
+ }
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[2],
+ strlen (args[2]),
+ &ctx->h_contract_terms,
+ sizeof (ctx->h_contract_terms)))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_CONTRACT_TERMS,
+ args[2]);
+ }
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[3],
+ strlen (args[3]),
+ &ctx->coin_pub,
+ sizeof (ctx->coin_pub)))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_COIN_PUB,
+ args[3]);
+ }
+ TALER_MHD_parse_request_arg_auto_t (rc->connection,
+ "merchant_sig",
+ &ctx->merchant_sig);
+ TALER_MHD_parse_request_timeout (rc->connection,
+ &ctx->timeout);
+ TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
+ {
+ if (GNUNET_OK !=
+ TALER_merchant_deposit_verify (&ctx->merchant,
+ &ctx->coin_pub,
+ &ctx->h_contract_terms,
+ &ctx->h_wire,
+ &ctx->merchant_sig))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_DEPOSITS_GET_MERCHANT_SIGNATURE_INVALID,
+ NULL);
+ }
}
}
- return handle_track_transaction_request (rc->connection,
- &ctx);
+ return handle_track_transaction_request (ctx);
}
diff --git a/src/exchange/taler-exchange-httpd_deposits_get.h b/src/exchange/taler-exchange-httpd_deposits_get.h
index aee7521a5..c7b1698bb 100644
--- a/src/exchange/taler-exchange-httpd_deposits_get.h
+++ b/src/exchange/taler-exchange-httpd_deposits_get.h
@@ -27,6 +27,13 @@
/**
+ * Resume long pollers on GET /deposits.
+ */
+void
+TEH_deposits_get_cleanup (void);
+
+
+/**
* Handle a "/deposits/$H_WIRE/$MERCHANT_PUB/$H_CONTRACT_TERMS/$COIN_PUB"
* request.
*
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index d73283509..fa87a3b84 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -2377,9 +2377,10 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
}
GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
- GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
}
+
+ GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
}
GNUNET_CONTAINER_heap_destroy (heap);
diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c
index bf4e4dea1..0372573bb 100644
--- a/src/exchange/taler-exchange-httpd_kyc-check.c
+++ b/src/exchange/taler-exchange-httpd_kyc-check.c
@@ -520,34 +520,8 @@ TEH_handler_kyc_check (
"usertype");
}
- {
- const char *ts;
-
- ts = MHD_lookup_connection_value (rc->connection,
- MHD_GET_ARGUMENT_KIND,
- "timeout_ms");
- if (NULL != ts)
- {
- char dummy;
- unsigned long long tms;
-
- if (1 !=
- sscanf (ts,
- "%llu%c",
- &tms,
- &dummy))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "timeout_ms");
- }
- kyp->timeout = GNUNET_TIME_relative_to_absolute (
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- tms));
- }
- }
+ TALER_MHD_parse_request_timeout (rc->connection,
+ &kyp->timeout);
}
if ( (NULL == kyp->eh) &&
diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c
index 6d06f0c82..9668ee54c 100644
--- a/src/exchange/taler-exchange-httpd_kyc-proof.c
+++ b/src/exchange/taler-exchange-httpd_kyc-proof.c
@@ -297,7 +297,6 @@ TEH_handler_kyc_proof (
{
struct KycProofContext *kpc = rc->rh_ctx;
const char *provider_section_or_logic = args[0];
- const char *h_payto;
if (NULL == kpc)
{
@@ -310,33 +309,13 @@ TEH_handler_kyc_proof (
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
"'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required");
}
- h_payto = MHD_lookup_connection_value (rc->connection,
- MHD_GET_ARGUMENT_KIND,
- "state");
- if (NULL == h_payto)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MISSING,
- "h_payto");
- }
kpc = GNUNET_new (struct KycProofContext);
kpc->rc = rc;
rc->rh_ctx = kpc;
rc->rh_cleaner = &clean_kpc;
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (h_payto,
- strlen (h_payto),
- &kpc->h_payto,
- sizeof (kpc->h_payto)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "h_payto");
- }
+ TALER_MHD_parse_request_arg_auto_t (rc->connection,
+ "state",
+ &kpc->h_payto);
if (GNUNET_OK !=
TALER_KYCLOGIC_lookup_logic (provider_section_or_logic,
&kpc->logic,
diff --git a/src/exchange/taler-exchange-httpd_management_wire_disable.c b/src/exchange/taler-exchange-httpd_management_wire_disable.c
index 34825eda3..077a56b25 100644
--- a/src/exchange/taler-exchange-httpd_management_wire_disable.c
+++ b/src/exchange/taler-exchange-httpd_management_wire_disable.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2020 Taler Systems SA
+ Copyright (C) 2020-2023 Taler Systems SA
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
@@ -114,6 +114,9 @@ del_wire (void *cls,
}
qs = TEH_plugin->update_wire (TEH_plugin->cls,
awc->payto_uri,
+ NULL,
+ NULL,
+ NULL,
awc->validity_end,
false);
if (qs < 0)
diff --git a/src/exchange/taler-exchange-httpd_management_wire_enable.c b/src/exchange/taler-exchange-httpd_management_wire_enable.c
index 25ee0eeac..6743b485c 100644
--- a/src/exchange/taler-exchange-httpd_management_wire_enable.c
+++ b/src/exchange/taler-exchange-httpd_management_wire_enable.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2020 Taler Systems SA
+ Copyright (C) 2020-2023 Taler Systems SA
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
@@ -55,6 +55,21 @@ struct AddWireContext
const char *payto_uri;
/**
+ * (optional) address of a conversion service for this account.
+ */
+ const char *conversion_url;
+
+ /**
+ * Restrictions imposed when crediting this account.
+ */
+ json_t *credit_restrictions;
+
+ /**
+ * Restrictions imposed when debiting this account.
+ */
+ json_t *debit_restrictions;
+
+ /**
* Timestamp for checking against replay attacks.
*/
struct GNUNET_TIME_Timestamp validity_start;
@@ -114,11 +129,17 @@ add_wire (void *cls,
if (0 == qs)
qs = TEH_plugin->insert_wire (TEH_plugin->cls,
awc->payto_uri,
+ awc->conversion_url,
+ awc->debit_restrictions,
+ awc->credit_restrictions,
awc->validity_start,
&awc->master_sig_wire);
else
qs = TEH_plugin->update_wire (TEH_plugin->cls,
awc->payto_uri,
+ awc->conversion_url,
+ awc->debit_restrictions,
+ awc->credit_restrictions,
awc->validity_start,
true);
if (qs < 0)
@@ -141,7 +162,9 @@ TEH_handler_management_post_wire (
struct MHD_Connection *connection,
const json_t *root)
{
- struct AddWireContext awc;
+ struct AddWireContext awc = {
+ .conversion_url = NULL
+ };
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("master_sig_wire",
&awc.master_sig_wire),
@@ -149,6 +172,14 @@ TEH_handler_management_post_wire (
&awc.master_sig_add),
GNUNET_JSON_spec_string ("payto_uri",
&awc.payto_uri),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("conversion_url",
+ &awc.conversion_url),
+ NULL),
+ GNUNET_JSON_spec_json ("credit_restrictions",
+ &awc.credit_restrictions),
+ GNUNET_JSON_spec_json ("debit_restrictions",
+ &awc.debit_restrictions),
GNUNET_JSON_spec_timestamp ("validity_start",
&awc.validity_start),
GNUNET_JSON_spec_end ()
@@ -179,17 +210,22 @@ TEH_handler_management_post_wire (
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
msg);
+ GNUNET_JSON_parse_free (spec);
GNUNET_free (msg);
return ret;
}
}
if (GNUNET_OK !=
TALER_exchange_offline_wire_add_verify (awc.payto_uri,
+ awc.conversion_url,
+ awc.debit_restrictions,
+ awc.credit_restrictions,
awc.validity_start,
&TEH_master_public_key,
&awc.master_sig_add))
{
GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_FORBIDDEN,
@@ -199,10 +235,14 @@ TEH_handler_management_post_wire (
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
TALER_exchange_wire_signature_check (awc.payto_uri,
+ awc.conversion_url,
+ awc.debit_restrictions,
+ awc.credit_restrictions,
&TEH_master_public_key,
&awc.master_sig_wire))
{
GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_FORBIDDEN,
@@ -218,6 +258,7 @@ TEH_handler_management_post_wire (
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"payto:// URI `%s' is malformed\n",
awc.payto_uri);
+ GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
@@ -237,6 +278,7 @@ TEH_handler_management_post_wire (
&ret,
&add_wire,
&awc);
+ GNUNET_JSON_parse_free (spec);
if (GNUNET_SYSERR == res)
return ret;
}
diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h
index 8f6804355..318113c1f 100644
--- a/src/exchange/taler-exchange-httpd_metrics.h
+++ b/src/exchange/taler-exchange-httpd_metrics.h
@@ -61,7 +61,8 @@ enum TEH_MetricTypeSuccess
TEH_MT_SUCCESS_BATCH_WITHDRAW = 3,
TEH_MT_SUCCESS_MELT = 4,
TEH_MT_SUCCESS_REFRESH_REVEAL = 5,
- TEH_MT_SUCCESS_COUNT = 6 /* MUST BE LAST! */
+ TEH_MT_SUCCESS_AGE_WITHDRAW_REVEAL = 6,
+ TEH_MT_SUCCESS_COUNT = 7 /* MUST BE LAST! */
};
/**
diff --git a/src/exchange/taler-exchange-httpd_purses_delete.c b/src/exchange/taler-exchange-httpd_purses_delete.c
index 58cc78250..5bf7c24c9 100644
--- a/src/exchange/taler-exchange-httpd_purses_delete.c
+++ b/src/exchange/taler-exchange-httpd_purses_delete.c
@@ -57,29 +57,9 @@ TEH_handler_purses_delete (
TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED,
args[0]);
}
- {
- const char *sig;
-
- sig = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- "Taler-Purse-Signature");
- if ( (NULL == sig) ||
- (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (sig,
- strlen (sig),
- &purse_sig,
- sizeof (purse_sig))) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- (NULL == sig)
- ? TALER_EC_GENERIC_PARAMETER_MISSING
- : TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "Taler-Purse-Signature");
- }
- }
-
+ TALER_MHD_parse_request_header_auto_t (connection,
+ "Taler-Purse-Signature",
+ &purse_sig);
if (GNUNET_OK !=
TALER_wallet_purse_delete_verify (&purse_pub,
&purse_sig))
diff --git a/src/exchange/taler-exchange-httpd_purses_get.c b/src/exchange/taler-exchange-httpd_purses_get.c
index 434798a80..613372357 100644
--- a/src/exchange/taler-exchange-httpd_purses_get.c
+++ b/src/exchange/taler-exchange-httpd_purses_get.c
@@ -243,36 +243,8 @@ TEH_handler_purses_get (struct TEH_RequestContext *rc,
args[1]);
}
- {
- const char *long_poll_timeout_ms;
-
- long_poll_timeout_ms
- = MHD_lookup_connection_value (rc->connection,
- MHD_GET_ARGUMENT_KIND,
- "timeout_ms");
- if (NULL != long_poll_timeout_ms)
- {
- unsigned int timeout_ms;
- char dummy;
- struct GNUNET_TIME_Relative timeout;
-
- if (1 != sscanf (long_poll_timeout_ms,
- "%u%c",
- &timeout_ms,
- &dummy))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "timeout_ms (must be non-negative number)");
- }
- timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- timeout_ms);
- gc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
- }
- }
-
+ TALER_MHD_parse_request_timeout (rc->connection,
+ &gc->timeout);
if ( (GNUNET_TIME_absolute_is_future (gc->timeout)) &&
(NULL == gc->eh) )
{
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index 08a85265c..89bdf2724 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -773,12 +773,17 @@ clean_age:
NULL);
goto cleanup;
}
+
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
+ {
rrcs[i].coin_sig = bss[i];
+ rrcs[i].blinded_planchet = rcds[i].blinded_planchet;
+ }
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Signatures ready, starting DB interaction\n");
+
for (unsigned int r = 0; r<MAX_TRANSACTION_COMMIT_RETRIES; r++)
{
bool changed;
@@ -795,12 +800,7 @@ clean_age:
NULL);
goto cleanup;
}
- for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
- {
- struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
- rrc->blinded_planchet = rcds[i].blinded_planchet;
- }
qs = TEH_plugin->insert_refresh_reveal (
TEH_plugin->cls,
melt_serial_id,
diff --git a/src/exchange/taler-exchange-httpd_reserves_get.c b/src/exchange/taler-exchange-httpd_reserves_get.c
index c22e62bf5..bbaac8535 100644
--- a/src/exchange/taler-exchange-httpd_reserves_get.c
+++ b/src/exchange/taler-exchange-httpd_reserves_get.c
@@ -52,8 +52,12 @@ struct ReservePoller
struct MHD_Connection *connection;
/**
- * Subscription for the database event we are
- * waiting for.
+ * Our request context.
+ */
+ struct TEH_RequestContext *rc;
+
+ /**
+ * Subscription for the database event we are waiting for.
*/
struct GNUNET_DB_EventHandler *eh;
@@ -154,6 +158,8 @@ db_event_cb (void *cls,
(void) extra_size;
if (! rp->suspended)
return; /* might get multiple wake-up events */
+ GNUNET_async_scope_enter (&rp->rc->async_scope_id,
+ &old_scope);
TEH_check_invariants ();
rp->suspended = false;
MHD_resume_connection (rp->connection);
@@ -171,11 +177,9 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
if (NULL == rp)
{
- struct GNUNET_TIME_Relative timeout
- = GNUNET_TIME_UNIT_ZERO;
-
rp = GNUNET_new (struct ReservePoller);
rp->connection = rc->connection;
+ rp->rc = rc;
rc->rh_ctx = rp;
rc->rh_cleaner = &rp_cleanup;
GNUNET_CONTAINER_DLL_insert (rp_head,
@@ -193,34 +197,8 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
args[0]);
}
- {
- const char *long_poll_timeout_ms;
-
- long_poll_timeout_ms
- = MHD_lookup_connection_value (rc->connection,
- MHD_GET_ARGUMENT_KIND,
- "timeout_ms");
- if (NULL != long_poll_timeout_ms)
- {
- unsigned int timeout_ms;
- char dummy;
-
- if (1 != sscanf (long_poll_timeout_ms,
- "%u%c",
- &timeout_ms,
- &dummy))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "timeout_ms (must be non-negative number)");
- }
- timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- timeout_ms);
- }
- }
- rp->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ TALER_MHD_parse_request_timeout (rc->connection,
+ &rp->timeout);
}
if ( (GNUNET_TIME_absolute_is_future (rp->timeout)) &&
diff --git a/src/exchange/taler-exchange-httpd_wire.c b/src/exchange/taler-exchange-httpd_wire.c
index 34010462d..17875a720 100644
--- a/src/exchange/taler-exchange-httpd_wire.c
+++ b/src/exchange/taler-exchange-httpd_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015-2022 Taler Systems SA
+ Copyright (C) 2015-2023 Taler Systems SA
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
@@ -224,12 +224,18 @@ TEH_wire_done ()
*
* @param cls a `json_t *` object to expand with wire account details
* @param payto_uri the exchange bank account URI to add
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account
+ * @param credit_restrictions JSON array with credit restrictions on the account
* @param master_sig master key signature affirming that this is a bank
* account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
*/
static void
add_wire_account (void *cls,
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
const struct TALER_MasterSignatureP *master_sig)
{
json_t *a = cls;
@@ -240,6 +246,13 @@ add_wire_account (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("payto_uri",
payto_uri),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("conversion_url",
+ conversion_url)),
+ GNUNET_JSON_pack_array_incref ("debit_restrictions",
+ (json_t *) debit_restrictions),
+ GNUNET_JSON_pack_array_incref ("credit_restrictions",
+ (json_t *) credit_restrictions),
GNUNET_JSON_pack_data_auto ("master_sig",
master_sig))))
{
@@ -462,6 +475,8 @@ build_wire_state (void)
wsh->wire_reply = TALER_MHD_MAKE_JSON_PACK (
GNUNET_JSON_pack_array_steal ("accounts",
wire_accounts_array),
+ GNUNET_JSON_pack_array_steal ("wads", /* #7271 */
+ json_array ()),
GNUNET_JSON_pack_object_steal ("fees",
wire_fee_object),
GNUNET_JSON_pack_data_auto ("master_public_key",
diff --git a/src/exchange/taler-exchange-transfer.c b/src/exchange/taler-exchange-transfer.c
index 5a4aace9c..255fe76a0 100644
--- a/src/exchange/taler-exchange-transfer.c
+++ b/src/exchange/taler-exchange-transfer.c
@@ -563,9 +563,9 @@ wire_prepare_cb (void *cls,
}
wpd = GNUNET_malloc (sizeof (struct WirePrepareData)
+ buf_size);
- memcpy (&wpd[1],
- buf,
- buf_size);
+ GNUNET_memcpy (&wpd[1],
+ buf,
+ buf_size);
wpd->buf_size = buf_size;
wpd->row_id = rowid;
GNUNET_CONTAINER_DLL_insert (wpd_head,
diff --git a/src/exchange/taler-exchange-wirewatch.c b/src/exchange/taler-exchange-wirewatch.c
index 235c0153f..047042423 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -731,8 +731,8 @@ history_cb (void *cls,
{
case MHD_HTTP_OK:
process_reply (wrap_size,
- reply->details.success.details,
- reply->details.success.details_length);
+ reply->details.ok.details,
+ reply->details.ok.details_length);
return;
case MHD_HTTP_NO_CONTENT:
transaction_completed ();
diff --git a/src/exchangedb/.gitignore b/src/exchangedb/.gitignore
index fa833d81f..6e67fadb1 100644
--- a/src/exchangedb/.gitignore
+++ b/src/exchangedb/.gitignore
@@ -12,4 +12,5 @@ test-exchangedb-batch-reserves-in-insert-postgres
test-exchangedb-by-j-postgres
test-exchangedb-populate-link-data-postgres
test-exchangedb-populate-ready-deposit-postgres
-test-exchangedb-populate-select-refunds-by-coin-postgres \ No newline at end of file
+test-exchangedb-populate-select-refunds-by-coin-postgres
+exchange-0004.sql
diff --git a/src/exchangedb/0003-withdraw_age_commitments.sql b/src/exchangedb/0003-age_withdraw_commitments.sql
index b8451129a..d74a697c3 100644
--- a/src/exchangedb/0003-withdraw_age_commitments.sql
+++ b/src/exchangedb/0003-age_withdraw_commitments.sql
@@ -14,24 +14,24 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
-CREATE FUNCTION create_table_withdraw_age_commitments(
+CREATE FUNCTION create_table_age_withdraw_commitments(
IN partition_suffix VARCHAR DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'withdraw_age_commitments';
+ table_name VARCHAR DEFAULT 'age_withdraw_commitments';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
- '(withdraw_age_commitment_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',h_commitment BYTEA PRIMARY KEY CHECK (LENGTH(h_commitment)=64)'
+ '(age_withdraw_commitment_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',h_commitment BYTEA CHECK (LENGTH(h_commitment)=64)'
',amount_with_fee_val INT8 NOT NULL'
',amount_with_fee_frac INT4 NOT NULL'
',max_age INT2 NOT NULL'
- ',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)'
- ',reserve_sig BYTEA CHECK (LENGTH(reserve_sig)=64)'
+ ',reserve_pub BYTEA CHECK (LENGTH(reserve_pub)=32)'
+ ',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
',noreveal_index INT4 NOT NULL'
') %s ;'
,table_name
@@ -77,66 +77,58 @@ END
$$;
-CREATE FUNCTION constrain_table_withdraw_age_commitments(
+CREATE FUNCTION constrain_table_age_withdraw_commitments(
IN partition_suffix VARCHAR
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'withdraw_age_commitments';
+ table_name VARCHAR DEFAULT 'age_withdraw_commitments';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
-
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
- ' ADD PRIMARY KEY (h_commitment, reserve_pub);'
+ ' ADD PRIMARY KEY (h_commitment);'
+ );
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_h_commitment_reserve_pub_key'
+ ' UNIQUE (h_commitment, reserve_pub);'
);
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_withdraw_age_commitment_id_key'
- ' UNIQUE (withdraw_age_commitment_id);'
+ ' ADD CONSTRAINT ' || table_name || '_age_withdraw_commitment_id_key'
+ ' UNIQUE (age_withdraw_commitment_id);'
);
END
$$;
-CREATE FUNCTION foreign_table_withdraw_age_commitments()
+CREATE FUNCTION foreign_table_age_withdraw_commitments()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'withdraw_age_commitments';
+ table_name VARCHAR DEFAULT 'age_withdraw_commitments';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
' FOREIGN KEY (reserve_pub)'
- ' REFERENCES reserves (reserve_pub) ON DELETE CASCADE;'
+ ' REFERENCES reserves(reserve_pub) ON DELETE CASCADE;'
);
END
$$;
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
- VALUES
- ('withdraw_age_commitments'
- ,'exchange-0003'
- ,'create'
- ,TRUE
- ,FALSE),
- ('withdraw_age_commitments'
- ,'exchange-0003'
- ,'constrain'
- ,TRUE
- ,FALSE),
- ('withdraw_age_commitments'
- ,'exchange-0003'
- ,'foreign'
- ,TRUE
- ,FALSE);
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+VALUES
+ ('age_withdraw_commitments', 'exchange-0003', 'create', TRUE ,FALSE),
+ ('age_withdraw_commitments', 'exchange-0003', 'constrain',TRUE ,FALSE),
+ ('age_withdraw_commitments', 'exchange-0003', 'foreign', TRUE ,FALSE);
diff --git a/src/exchangedb/0003-withdraw_age_reveals.sql b/src/exchangedb/0003-age_withdraw_reveals.sql
index af66eab75..1c55fb190 100644
--- a/src/exchangedb/0003-withdraw_age_reveals.sql
+++ b/src/exchangedb/0003-age_withdraw_reveals.sql
@@ -14,25 +14,24 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
-CREATE FUNCTION create_table_withdraw_age_revealed_coins(
+CREATE FUNCTION create_table_age_withdraw_revealed_coins(
IN partition_suffix VARCHAR DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'withdraw_age_revealed_coins';
+ table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
- '(withdraw_age_revealed_coins_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE
+ '(age_withdraw_revealed_coins_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE
',h_commitment BYTEA NOT NULL CHECK (LENGTH(h_commitment)=64)'
',freshcoin_index INT4 NOT NULL'
',denominations_serial INT8 NOT NULL'
',coin_ev BYTEA NOT NULL'
',h_coin_ev BYTEA CHECK (LENGTH(h_coin_ev)=64)'
',ev_sig BYTEA NOT NULL'
- ',ewv BYTEA NOT NULL'
') %s ;'
,table_name
,'PARTITION BY HASH (h_commitment)'
@@ -79,30 +78,24 @@ BEGIN
,table_name
,partition_suffix
);
- PERFORM comment_partitioned_column(
- 'Exchange contributed values in the creation of the fresh coin (see /csr)'
- ,'ewv'
- ,table_name
- ,partition_suffix
- );
END
$$;
-CREATE FUNCTION constrain_table_withdraw_age_revealed_coins(
+CREATE FUNCTION constrain_table_age_withdraw_revealed_coins(
IN partition_suffix VARCHAR
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'withdraw_age_revealed_coins';
+ table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_withdraw_age_revealed_coins_id_key'
- ' UNIQUE (withdraw_age_revealed_coins_id);'
+ ' ADD CONSTRAINT ' || table_name || '_age_withdraw_revealed_coins_id_key'
+ ' UNIQUE (age_withdraw_revealed_coins_id);'
);
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -112,18 +105,18 @@ BEGIN
END
$$;
-CREATE FUNCTION foreign_table_withdraw_age_revealed_coins()
+CREATE FUNCTION foreign_table_age_withdraw_revealed_coins()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'withdraw_age_revealed_coins';
+ table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_foreign_h_commitment'
' FOREIGN KEY (h_commitment)'
- ' REFERENCES withdraw_age_commitments (h_commitment) ON DELETE CASCADE;'
+ ' REFERENCES age_withdraw_commitments (h_commitment) ON DELETE CASCADE;'
);
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -142,17 +135,17 @@ INSERT INTO exchange_tables
,partitioned
,by_range)
VALUES
- ('withdraw_age_revealed_coins'
+ ('age_withdraw_revealed_coins'
,'exchange-0003'
,'create'
,TRUE
,FALSE),
- ('withdraw_age_revealed_coins'
+ ('age_withdraw_revealed_coins'
,'exchange-0003'
,'constrain'
,TRUE
,FALSE),
- ('withdraw_age_revealed_coins'
+ ('age_withdraw_revealed_coins'
,'exchange-0003'
,'foreign'
,TRUE
diff --git a/src/exchangedb/0004-wire_accounts.sql b/src/exchangedb/0004-wire_accounts.sql
new file mode 100644
index 000000000..6114c821a
--- /dev/null
+++ b/src/exchangedb/0004-wire_accounts.sql
@@ -0,0 +1,26 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 Taler Systems SA
+--
+-- 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, see <http://www.gnu.org/licenses/>
+--
+
+ALTER TABLE wire_accounts
+ ADD COLUMN conversion_url VARCHAR DEFAULT (NULL),
+ ADD COLUMN debit_restrictions VARCHAR DEFAULT (NULL),
+ ADD COLUMN credit_restrictions VARCHAR DEFAULT (NULL);
+COMMENT ON COLUMN wire_accounts.conversion_url
+ IS 'URL of a currency conversion service if conversion is needed when this account is used; NULL if there is no conversion.';
+COMMENT ON COLUMN wire_accounts.debit_restrictions
+ IS 'JSON array describing restrictions imposed when debiting this account. Empty for no restrictions, NULL if account was migrated from previous database revision or account is disabled.';
+COMMENT ON COLUMN wire_accounts.credit_restrictions
+ IS 'JSON array describing restrictions imposed when crediting this account. Empty for no restrictions, NULL if account was migrated from previous database revision or account is disabled.';
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index de76997cb..ee78b87f7 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -20,7 +20,9 @@ sqlinputs = \
0002-*.sql \
exchange-0002.sql.in \
0003-*.sql \
- exchange-0003.sql.in
+ exchange-0003.sql.in \
+ 0004-*.sql \
+ exchange-0004.sql.in
sql_DATA = \
benchmark-0001.sql \
@@ -28,6 +30,7 @@ sql_DATA = \
exchange-0001.sql \
exchange-0002.sql \
exchange-0003.sql \
+ exchange-0004.sql \
drop.sql \
procedures.sql
@@ -39,7 +42,8 @@ BUILT_SOURCES = \
CLEANFILES = \
exchange-0002.sql \
- exchange-0003.sql
+ exchange-0003.sql \
+ exchange-0004.sql
procedures.sql: procedures.sql.in exchange_do_*.sql
chmod +w $@ || true
@@ -56,6 +60,11 @@ exchange-0003.sql: exchange-0003.sql.in 0003-*.sql
gcc -E -P -undef - < exchange-0003.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
+exchange-0004.sql: exchange-0004.sql.in 0004-*.sql
+ chmod +w $@ || true
+ gcc -E -P -undef - < exchange-0004.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
+ chmod ugo-w $@
+
EXTRA_DIST = \
exchangedb.conf \
exchangedb-postgres.conf \
diff --git a/src/exchangedb/bench_db.c b/src/exchangedb/bench_db.c
index a85834d13..302d23062 100644
--- a/src/exchangedb/bench_db.c
+++ b/src/exchangedb/bench_db.c
@@ -169,9 +169,9 @@ bem_insert (struct GNUNET_PQ_Context *conn,
GNUNET_CRYPTO_hash (&b,
sizeof (b),
&hc);
- memcpy (&ihc,
- &hc,
- sizeof (ihc));
+ GNUNET_memcpy (&ihc,
+ &hc,
+ sizeof (ihc));
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&hc),
@@ -265,9 +265,9 @@ bem_select (struct GNUNET_PQ_Context *conn,
GNUNET_CRYPTO_hash (&b,
sizeof (b),
&hc);
- memcpy (&ihc,
- &hc,
- sizeof (ihc));
+ GNUNET_memcpy (&ihc,
+ &hc,
+ sizeof (ihc));
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint32 (&ihc),
diff --git a/src/exchangedb/drop.sql b/src/exchangedb/drop.sql
index ecebde6f5..843cda8ec 100644
--- a/src/exchangedb/drop.sql
+++ b/src/exchangedb/drop.sql
@@ -21,6 +21,7 @@ BEGIN;
SELECT _v.unregister_patch('exchange-0001');
SELECT _v.unregister_patch('exchange-0002');
SELECT _v.unregister_patch('exchange-0003');
+SELECT _v.unregister_patch('exchange-0004');
DROP SCHEMA exchange CASCADE;
diff --git a/src/exchangedb/exchange-0003.sql.in b/src/exchangedb/exchange-0003.sql.in
index 5461c0dd3..01733ea24 100644
--- a/src/exchangedb/exchange-0003.sql.in
+++ b/src/exchangedb/exchange-0003.sql.in
@@ -25,6 +25,8 @@ SET search_path TO exchange;
#include "0003-aml_status.sql"
#include "0003-aml_staff.sql"
#include "0003-aml_history.sql"
+#include "0003-age_withdraw_commitments.sql"
+#include "0003-age_withdraw_reveals.sql"
COMMIT;
diff --git a/src/exchangedb/exchange-0004.sql.in b/src/exchangedb/exchange-0004.sql.in
new file mode 100644
index 000000000..00979e193
--- /dev/null
+++ b/src/exchangedb/exchange-0004.sql.in
@@ -0,0 +1,24 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--2023 Taler Systems SA
+--
+-- 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, see <http://www.gnu.org/licenses/>
+--
+
+BEGIN;
+
+SELECT _v.register_patch('exchange-0004', NULL, NULL);
+SET search_path TO exchange;
+
+#include "0004-wire_accounts.sql"
+
+COMMIT;
diff --git a/src/exchangedb/pg_aggregate.c b/src/exchangedb/pg_aggregate.c
index 6e94cbebb..76d0adec3 100644
--- a/src/exchangedb/pg_aggregate.c
+++ b/src/exchangedb/pg_aggregate.c
@@ -22,6 +22,7 @@
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
+#include "pg_event_notify.h"
#include "pg_aggregate.h"
#include "pg_helper.h"
@@ -35,34 +36,12 @@ TEH_PG_aggregate (
{
struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Absolute now = {0};
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_absolute_time (&now),
- GNUNET_PQ_query_param_auto_from_type (merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_end
- };
uint64_t sum_deposit_value;
uint64_t sum_deposit_frac;
uint64_t sum_refund_value;
uint64_t sum_refund_frac;
uint64_t sum_fee_value;
uint64_t sum_fee_frac;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("sum_deposit_value",
- &sum_deposit_value),
- GNUNET_PQ_result_spec_uint64 ("sum_deposit_fraction",
- &sum_deposit_frac),
- GNUNET_PQ_result_spec_uint64 ("sum_refund_value",
- &sum_refund_value),
- GNUNET_PQ_result_spec_uint64 ("sum_refund_fraction",
- &sum_refund_frac),
- GNUNET_PQ_result_spec_uint64 ("sum_fee_value",
- &sum_fee_value),
- GNUNET_PQ_result_spec_uint64 ("sum_fee_fraction",
- &sum_fee_frac),
- GNUNET_PQ_result_spec_end
- };
enum GNUNET_DB_QueryStatus qs;
struct TALER_Amount sum_deposit;
struct TALER_Amount sum_refund;
@@ -71,8 +50,6 @@ TEH_PG_aggregate (
now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
pg->aggregator_shift);
-
- /* Used in #postgres_aggregate() */
PREPARE (pg,
"aggregate",
"WITH dep AS (" /* restrict to our merchant and account and mark as done */
@@ -148,11 +125,35 @@ TEH_PG_aggregate (
" FULL OUTER JOIN ref ON (FALSE)" /* We just want all sums */
" FULL OUTER JOIN fees ON (FALSE);");
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_absolute_time (&now),
+ GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_auto_from_type (wtid),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("sum_deposit_value",
+ &sum_deposit_value),
+ GNUNET_PQ_result_spec_uint64 ("sum_deposit_fraction",
+ &sum_deposit_frac),
+ GNUNET_PQ_result_spec_uint64 ("sum_refund_value",
+ &sum_refund_value),
+ GNUNET_PQ_result_spec_uint64 ("sum_refund_fraction",
+ &sum_refund_frac),
+ GNUNET_PQ_result_spec_uint64 ("sum_fee_value",
+ &sum_fee_value),
+ GNUNET_PQ_result_spec_uint64 ("sum_fee_fraction",
+ &sum_fee_frac),
+ GNUNET_PQ_result_spec_end
+ };
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "aggregate",
- params,
- rs);
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "aggregate",
+ params,
+ rs);
+ }
if (qs < 0)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
@@ -165,6 +166,18 @@ TEH_PG_aggregate (
total));
return qs;
}
+ {
+ struct TALER_CoinDepositEventP rep = {
+ .header.size = htons (sizeof (rep)),
+ .header.type = htons (TALER_DBEVENT_EXCHANGE_DEPOSIT_STATUS_CHANGED),
+ .merchant_pub = *merchant_pub
+ };
+
+ TEH_PG_event_notify (pg,
+ &rep.header,
+ NULL,
+ 0);
+ }
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (pg->currency,
&sum_deposit));
diff --git a/src/exchangedb/pg_get_age_withdraw_info.c b/src/exchangedb/pg_get_age_withdraw_info.c
index 754b572c9..f4a68b377 100644
--- a/src/exchangedb/pg_get_age_withdraw_info.c
+++ b/src/exchangedb/pg_get_age_withdraw_info.c
@@ -69,7 +69,7 @@ TEH_PG_get_age_withdraw_info (
",amount_with_fee_val"
",amount_with_fee_frac"
",noreveal_index"
- " FROM withdraw_age_commitments"
+ " FROM age_withdraw_commitments"
" WHERE reserve_pub=$1 and h_commitment=$2;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_age_withdraw_info",
diff --git a/src/exchangedb/pg_get_wire_accounts.c b/src/exchangedb/pg_get_wire_accounts.c
index 43590acf8..23b939046 100644
--- a/src/exchangedb/pg_get_wire_accounts.c
+++ b/src/exchangedb/pg_get_wire_accounts.c
@@ -66,10 +66,25 @@ get_wire_accounts_cb (void *cls,
for (unsigned int i = 0; i < num_results; i++)
{
char *payto_uri;
+ char *conversion_url = NULL;
+ json_t *debit_restrictions = NULL;
+ json_t *credit_restrictions = NULL;
struct TALER_MasterSignatureP master_sig;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("payto_uri",
&payto_uri),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("conversion_url",
+ &conversion_url),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_result_spec_json ("debit_restrictions",
+ &debit_restrictions),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_result_spec_json ("credit_restrictions",
+ &credit_restrictions),
+ NULL),
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
&master_sig),
GNUNET_PQ_result_spec_end
@@ -84,8 +99,21 @@ get_wire_accounts_cb (void *cls,
ctx->status = GNUNET_SYSERR;
return;
}
+ if (NULL == debit_restrictions)
+ {
+ debit_restrictions = json_array ();
+ GNUNET_assert (NULL != debit_restrictions);
+ }
+ if (NULL == credit_restrictions)
+ {
+ credit_restrictions = json_array ();
+ GNUNET_assert (NULL != credit_restrictions);
+ }
ctx->cb (ctx->cb_cls,
payto_uri,
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
&master_sig);
GNUNET_PQ_cleanup_result (rs);
}
@@ -112,6 +140,9 @@ TEH_PG_get_wire_accounts (void *cls,
"get_wire_accounts",
"SELECT"
" payto_uri"
+ ",conversion_url"
+ ",debit_restrictions"
+ ",credit_restrictions"
",master_sig"
" FROM wire_accounts"
" WHERE is_active");
@@ -123,5 +154,4 @@ TEH_PG_get_wire_accounts (void *cls,
if (GNUNET_OK != ctx.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
-
}
diff --git a/src/exchangedb/pg_insert_age_withdraw_reveal.c b/src/exchangedb/pg_insert_age_withdraw_reveal.c
index 336ed384f..ebba7ebbc 100644
--- a/src/exchangedb/pg_insert_age_withdraw_reveal.c
+++ b/src/exchangedb/pg_insert_age_withdraw_reveal.c
@@ -42,8 +42,8 @@ TEH_PG_insert_age_withdraw_reveal (
/* TODO */
#if 0
PREPARE (pg,
- "insert_withdraw_age_revealed_coin",
- "INSERT INTO withdraw_age_reveals "
+ "insert_age_withdraw_revealed_coin",
+ "INSERT INTO age_withdraw_reveals "
"(h_commitment "
",freshcoin_index "
",denominations_serial "
diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c
index 3ec9c77cc..e16a4b74f 100644
--- a/src/exchangedb/pg_insert_records_by_table.c
+++ b/src/exchangedb/pg_insert_records_by_table.c
@@ -2062,39 +2062,39 @@ irbt_cb_table_purse_deletion (struct PostgresClosure *pg,
/**
- * Function called with withdraw_age_commitments records to insert into table.
+ * Function called with age_withdraw_commitments records to insert into table.
*
* @param pg plugin context
* @param td record to insert
*/
static enum GNUNET_DB_QueryStatus
-irbt_cb_table_withdraw_age_commitments (struct PostgresClosure *pg,
+irbt_cb_table_age_withdraw_commitments (struct PostgresClosure *pg,
const struct
TALER_EXCHANGEDB_TableData *td)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (
- &td->details.withdraw_age_commitments.h_commitment),
+ &td->details.age_withdraw_commitments.h_commitment),
TALER_PQ_query_param_amount (
- &td->details.withdraw_age_commitments.amount_with_fee),
+ &td->details.age_withdraw_commitments.amount_with_fee),
GNUNET_PQ_query_param_uint16 (
- &td->details.withdraw_age_commitments.max_age),
+ &td->details.age_withdraw_commitments.max_age),
GNUNET_PQ_query_param_auto_from_type (
- &td->details.withdraw_age_commitments.reserve_pub),
+ &td->details.age_withdraw_commitments.reserve_pub),
GNUNET_PQ_query_param_auto_from_type (
- &td->details.withdraw_age_commitments.reserve_sig),
+ &td->details.age_withdraw_commitments.reserve_sig),
GNUNET_PQ_query_param_uint32 (
- &td->details.withdraw_age_commitments.noreveal_index),
+ &td->details.age_withdraw_commitments.noreveal_index),
GNUNET_PQ_query_param_absolute_time (
- &td->details.withdraw_age_commitments.timestamp),
+ &td->details.age_withdraw_commitments.timestamp),
GNUNET_PQ_query_param_end
};
PREPARE (pg,
- "insert_into_table_withdraw_age_commitments",
- "INSERT INTO withdraw_age_commitments"
- "(withdraw_age_commitment_id"
+ "insert_into_table_age_withdraw_commitments",
+ "INSERT INTO age_withdraw_commitments"
+ "(age_withdraw_commitment_id"
",h_commitment"
",amount_with_fee_val"
",amount_with_fee_frac"
@@ -2106,19 +2106,19 @@ irbt_cb_table_withdraw_age_commitments (struct PostgresClosure *pg,
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_into_table_withdraw_age_commitments",
+ "insert_into_table_age_withdraw_commitments",
params);
}
/**
- * Function called with withdraw_age_revealed_coins records to insert into table.
+ * Function called with age_withdraw_revealed_coins records to insert into table.
*
* @param pg plugin context
* @param td record to insert
*/
static enum GNUNET_DB_QueryStatus
-irbt_cb_table_withdraw_age_revealed_coins (struct PostgresClosure *pg,
+irbt_cb_table_age_withdraw_revealed_coins (struct PostgresClosure *pg,
const struct
TALER_EXCHANGEDB_TableData *td)
{
@@ -2126,26 +2126,24 @@ irbt_cb_table_withdraw_age_revealed_coins (struct PostgresClosure *pg,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (
- &td->details.withdraw_age_revealed_coins.h_commitment),
+ &td->details.age_withdraw_revealed_coins.h_commitment),
GNUNET_PQ_query_param_uint32 (
- &td->details.withdraw_age_revealed_coins.freshcoin_index),
+ &td->details.age_withdraw_revealed_coins.freshcoin_index),
GNUNET_PQ_query_param_uint64 (
- &td->details.withdraw_age_revealed_coins.denominations_serial),
+ &td->details.age_withdraw_revealed_coins.denominations_serial),
GNUNET_PQ_query_param_fixed_size (
- td->details.withdraw_age_revealed_coins.coin_ev,
- td->details.withdraw_age_revealed_coins.coin_ev_size),
+ td->details.age_withdraw_revealed_coins.coin_ev,
+ td->details.age_withdraw_revealed_coins.coin_ev_size),
GNUNET_PQ_query_param_auto_from_type (&h_coin_ev),
TALER_PQ_query_param_blinded_denom_sig (
- &td->details.withdraw_age_revealed_coins.ev_sig),
- TALER_PQ_query_param_exchange_withdraw_values (
- &td->details.withdraw_age_revealed_coins.ewv),
+ &td->details.age_withdraw_revealed_coins.ev_sig),
GNUNET_PQ_query_param_end
};
PREPARE (pg,
- "insert_into_table_withdraw_age_revealed_coins",
- "INSERT INTO withdraw_age_revealed_coins"
- "(withdraw_age_revealed_coins_id"
+ "insert_into_table_age_withdraw_revealed_coins",
+ "INSERT INTO age_withdraw_revealed_coins"
+ "(age_withdraw_revealed_coins_id"
",h_commitment"
",freshcoin_index"
",denominations_serial"
@@ -2156,12 +2154,12 @@ irbt_cb_table_withdraw_age_revealed_coins (struct PostgresClosure *pg,
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8);");
- GNUNET_CRYPTO_hash (td->details.withdraw_age_revealed_coins.coin_ev,
- td->details.withdraw_age_revealed_coins.coin_ev_size,
+ GNUNET_CRYPTO_hash (td->details.age_withdraw_revealed_coins.coin_ev,
+ td->details.age_withdraw_revealed_coins.coin_ev_size,
&h_coin_ev);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_into_table_withdraw_age_revealed_coins",
+ "insert_into_table_age_withdraw_revealed_coins",
params);
}
@@ -2314,10 +2312,10 @@ TEH_PG_insert_records_by_table (void *cls,
rh = &irbt_cb_table_purse_deletion;
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
- rh = &irbt_cb_table_withdraw_age_commitments;
+ rh = &irbt_cb_table_age_withdraw_commitments;
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
- rh = &irbt_cb_table_withdraw_age_revealed_coins;
+ rh = &irbt_cb_table_age_withdraw_revealed_coins;
break;
}
if (NULL == rh)
diff --git a/src/exchangedb/pg_insert_wire.c b/src/exchangedb/pg_insert_wire.c
index 75323b6fc..8329a04af 100644
--- a/src/exchangedb/pg_insert_wire.c
+++ b/src/exchangedb/pg_insert_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2023 Taler Systems SA
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
@@ -29,12 +29,20 @@
enum GNUNET_DB_QueryStatus
TEH_PG_insert_wire (void *cls,
const char *payto_uri,
+ const char *conversion_url,
+ json_t *debit_restrictions,
+ json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp start_date,
const struct TALER_MasterSignatureP *master_sig)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (payto_uri),
+ NULL == conversion_url
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (conversion_url),
+ TALER_PQ_query_param_json (debit_restrictions),
+ TALER_PQ_query_param_json (credit_restrictions),
GNUNET_PQ_query_param_auto_from_type (master_sig),
GNUNET_PQ_query_param_timestamp (&start_date),
GNUNET_PQ_query_param_end
@@ -44,11 +52,14 @@ TEH_PG_insert_wire (void *cls,
"insert_wire",
"INSERT INTO wire_accounts "
"(payto_uri"
+ ",conversion_url"
+ ",debit_restrictions"
+ ",credit_restrictions"
",master_sig"
",is_active"
",last_change"
") VALUES "
- "($1, $2, true, $3);");
+ "($1, $2, $3, $4, $5, true, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_wire",
params);
diff --git a/src/exchangedb/pg_insert_wire.h b/src/exchangedb/pg_insert_wire.h
index 670928d7c..c949327d7 100644
--- a/src/exchangedb/pg_insert_wire.h
+++ b/src/exchangedb/pg_insert_wire.h
@@ -29,6 +29,9 @@
*
* @param cls closure
* @param payto_uri wire account of the exchange
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account
+ * @param credit_restrictions JSON array with credit restrictions on the account
* @param start_date date when the account was added by the offline system
* (only to be used for replay detection)
* @param master_sig public signature affirming the existence of the account,
@@ -38,6 +41,9 @@
enum GNUNET_DB_QueryStatus
TEH_PG_insert_wire (void *cls,
const char *payto_uri,
+ const char *conversion_url,
+ json_t *debit_restrictions,
+ json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp start_date,
const struct TALER_MasterSignatureP *master_sig);
diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c
index 2e157360f..534e9a1d2 100644
--- a/src/exchangedb/pg_lookup_records_by_table.c
+++ b/src/exchangedb/pg_lookup_records_by_table.c
@@ -1118,9 +1118,9 @@ lrbt_cb_table_refresh_transfer_keys (void *cls,
ctx->error = true;
return;
}
- memcpy (&td.details.refresh_transfer_keys.tprivs[0],
- tpriv,
- tpriv_size);
+ GNUNET_memcpy (&td.details.refresh_transfer_keys.tprivs[0],
+ tpriv,
+ tpriv_size);
ctx->cb (ctx->cb_cls,
&td);
GNUNET_PQ_cleanup_result (rs);
@@ -2767,14 +2767,14 @@ lrbt_cb_table_purse_deletion (void *cls,
/**
- * Function called with withdraw_age_commitments table entries.
+ * Function called with age_withdraw_commitments table entries.
*
* @param cls closure
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
-lrbt_cb_table_withdraw_age_commitments (void *cls,
+lrbt_cb_table_age_withdraw_commitments (void *cls,
PGresult *result,
unsigned int num_results)
{
@@ -2788,26 +2788,26 @@ lrbt_cb_table_withdraw_age_commitments (void *cls,
{
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 (
- "withdraw_age_commitment_id",
+ "age_withdraw_commitment_id",
&td.serial),
GNUNET_PQ_result_spec_auto_from_type (
"h_commitment",
- &td.details.withdraw_age_commitments.h_commitment),
+ &td.details.age_withdraw_commitments.h_commitment),
GNUNET_PQ_result_spec_uint16 (
"max_age",
- &td.details.withdraw_age_commitments.max_age),
+ &td.details.age_withdraw_commitments.max_age),
TALER_PQ_RESULT_SPEC_AMOUNT (
"amount_with_fee",
- &td.details.withdraw_age_commitments.amount_with_fee),
+ &td.details.age_withdraw_commitments.amount_with_fee),
GNUNET_PQ_result_spec_auto_from_type (
"reserve_pub",
- &td.details.withdraw_age_commitments.reserve_pub),
+ &td.details.age_withdraw_commitments.reserve_pub),
GNUNET_PQ_result_spec_auto_from_type (
"reserve_sig",
- &td.details.withdraw_age_commitments.reserve_sig),
+ &td.details.age_withdraw_commitments.reserve_sig),
GNUNET_PQ_result_spec_uint32 (
"noreveal_index",
- &td.details.withdraw_age_commitments.noreveal_index),
+ &td.details.age_withdraw_commitments.noreveal_index),
GNUNET_PQ_result_spec_end
};
@@ -2828,14 +2828,14 @@ lrbt_cb_table_withdraw_age_commitments (void *cls,
/**
- * Function called with withdraw_age_revealed_coins table entries.
+ * Function called with age_withdraw_revealed_coins table entries.
*
* @param cls closure
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
-lrbt_cb_table_withdraw_age_revealed_coins (void *cls,
+lrbt_cb_table_age_withdraw_revealed_coins (void *cls,
PGresult *result,
unsigned int num_results)
{
@@ -2848,22 +2848,22 @@ lrbt_cb_table_withdraw_age_revealed_coins (void *cls,
{
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 (
- "withdraw_age_revealed_coins_id",
+ "age_withdraw_revealed_coins_id",
&td.serial),
GNUNET_PQ_result_spec_auto_from_type (
"h_commitment",
- &td.details.withdraw_age_revealed_coins.h_commitment),
+ &td.details.age_withdraw_revealed_coins.h_commitment),
GNUNET_PQ_result_spec_uint32 (
"freshcoin_index",
- &td.details.withdraw_age_revealed_coins.freshcoin_index),
+ &td.details.age_withdraw_revealed_coins.freshcoin_index),
GNUNET_PQ_result_spec_uint64 (
"denominations_serial",
- &td.details.withdraw_age_revealed_coins.denominations_serial),
+ &td.details.age_withdraw_revealed_coins.denominations_serial),
/* Note: h_coin_ev is recalculated */
GNUNET_PQ_result_spec_variable_size (
"coin_ev",
- (void **) &td.details.withdraw_age_revealed_coins.coin_ev,
- &td.details.withdraw_age_revealed_coins.coin_ev_size),
+ (void **) &td.details.age_withdraw_revealed_coins.coin_ev,
+ &td.details.age_withdraw_revealed_coins.coin_ev_size),
TALER_PQ_result_spec_blinded_denom_sig (
"ev_sig",
&td.details.refresh_revealed_coins.ev_sig),
@@ -3598,9 +3598,9 @@ TEH_PG_lookup_records_by_table (void *cls,
rh = &lrbt_cb_table_purse_deletion;
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
- XPREPARE ("select_above_serial_by_table_withdraw_age_commitments",
+ XPREPARE ("select_above_serial_by_table_age_withdraw_commitments",
"SELECT"
- " withdraw_age_commitment_id"
+ " age_withdraw_commitment_id"
",h_commitment"
",amount_with_fee_val"
",amount_with_fee_frac"
@@ -3608,15 +3608,15 @@ TEH_PG_lookup_records_by_table (void *cls,
",reserve_pub"
",reserve_sig"
",noreveal_index"
- " FROM withdraw_age_commitments"
- " WHERE withdraw_age_commitment_id > $1"
- " ORDER BY withdraw_age_commitment_id ASC;");
- rh = &lrbt_cb_table_withdraw_age_commitments;
+ " FROM age_withdraw_commitments"
+ " WHERE age_withdraw_commitment_id > $1"
+ " ORDER BY age_withdraw_commitment_id ASC;");
+ rh = &lrbt_cb_table_age_withdraw_commitments;
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
- XPREPARE ("select_above_serial_by_table_withdraw_age_revealed_coins",
+ XPREPARE ("select_above_serial_by_table_age_withdraw_revealed_coins",
"SELECT"
- " withdraw_age_revealed_coins_serial_id"
+ " age_withdraw_revealed_coins_serial_id"
",h_commitment"
",freshcoin_index"
",denominations_serial"
@@ -3624,10 +3624,10 @@ TEH_PG_lookup_records_by_table (void *cls,
",h_coin_ev"
",ev_sig"
",ewv"
- " FROM withdraw_age_revealed_coins"
- " WHERE withdraw_age_revealed_coins_serial_id > $1"
- " ORDER BY withdraw_age_revealed_coins_serial_id ASC;");
- rh = &lrbt_cb_table_withdraw_age_revealed_coins;
+ " FROM age_withdraw_revealed_coins"
+ " WHERE age_withdraw_revealed_coins_serial_id > $1"
+ " ORDER BY age_withdraw_revealed_coins_serial_id ASC;");
+ rh = &lrbt_cb_table_age_withdraw_revealed_coins;
break;
}
if (NULL == rh)
diff --git a/src/exchangedb/pg_lookup_serial_by_table.c b/src/exchangedb/pg_lookup_serial_by_table.c
index c98b4539e..2e3b41304 100644
--- a/src/exchangedb/pg_lookup_serial_by_table.c
+++ b/src/exchangedb/pg_lookup_serial_by_table.c
@@ -427,22 +427,22 @@ TEH_PG_lookup_serial_by_table (void *cls,
statement = "select_serial_by_table_purse_deletion";
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
- XPREPARE ("select_serial_by_table_withdraw_age_commitments",
+ XPREPARE ("select_serial_by_table_age_withdraw_commitments",
"SELECT"
- " withdraw_age_commitment_id AS serial"
- " FROM withdraw_age_commitments"
- " ORDER BY withdraw_age_commitment_id DESC"
+ " age_withdraw_commitment_id AS serial"
+ " FROM age_withdraw_commitments"
+ " ORDER BY age_withdraw_commitment_id DESC"
" LIMIT 1;");
- statement = "select_serial_by_table_withdraw_age_commitments";
+ statement = "select_serial_by_table_age_withdraw_commitments";
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
- XPREPARE ("select_serial_by_table_withdraw_age_revealed_coins",
+ XPREPARE ("select_serial_by_table_age_withdraw_revealed_coins",
"SELECT"
- " withdraw_age_revealed_coins_id AS serial"
- " FROM withdraw_age_revealed_coins"
- " ORDER BY withdraw_age_revealed_coins_id DESC"
+ " age_withdraw_revealed_coins_id AS serial"
+ " FROM age_withdraw_revealed_coins"
+ " ORDER BY age_withdraw_revealed_coins_id DESC"
" LIMIT 1;");
- statement = "select_serial_by_table_withdraw_age_revealed_coins";
+ statement = "select_serial_by_table_age_withdraw_revealed_coins";
break;
}
if (NULL == statement)
diff --git a/src/exchangedb/pg_update_wire.c b/src/exchangedb/pg_update_wire.c
index 4059348c9..0c4ec7b58 100644
--- a/src/exchangedb/pg_update_wire.c
+++ b/src/exchangedb/pg_update_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2023 Taler Systems SA
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
@@ -29,6 +29,9 @@
enum GNUNET_DB_QueryStatus
TEH_PG_update_wire (void *cls,
const char *payto_uri,
+ const char *conversion_url,
+ json_t *debit_restrictions,
+ json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp change_date,
bool enabled)
{
@@ -36,17 +39,28 @@ TEH_PG_update_wire (void *cls,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (payto_uri),
GNUNET_PQ_query_param_bool (enabled),
+ NULL == conversion_url
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (conversion_url),
+ enabled
+ ? TALER_PQ_query_param_json (debit_restrictions)
+ : GNUNET_PQ_query_param_null (),
+ enabled
+ ? TALER_PQ_query_param_json (credit_restrictions)
+ : GNUNET_PQ_query_param_null (),
GNUNET_PQ_query_param_timestamp (&change_date),
GNUNET_PQ_query_param_end
};
- /* used in #postgres_update_wire() */
PREPARE (pg,
"update_wire",
"UPDATE wire_accounts"
" SET"
" is_active=$2"
- " ,last_change=$3"
+ " ,conversion_url=$3"
+ " ,debit_restrictions=$4"
+ " ,credit_restrictions=$5"
+ " ,last_change=$6"
" WHERE payto_uri=$1");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_wire",
diff --git a/src/exchangedb/pg_update_wire.h b/src/exchangedb/pg_update_wire.h
index 67038b580..360b8845a 100644
--- a/src/exchangedb/pg_update_wire.h
+++ b/src/exchangedb/pg_update_wire.h
@@ -24,11 +24,16 @@
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
+
/**
* Update information about a wire account of the exchange.
*
* @param cls closure
* @param payto_uri account the update is about
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account; NULL allowed if not @a enabled
+ * @param credit_restrictions JSON array with credit restrictions on the account; NULL allowed if not @a enabled
* @param change_date date when the account status was last changed
* (only to be used for replay detection)
* @param enabled true to enable, false to disable (the actual change)
@@ -37,6 +42,9 @@
enum GNUNET_DB_QueryStatus
TEH_PG_update_wire (void *cls,
const char *payto_uri,
+ const char *conversion_url,
+ json_t *debit_restrictions,
+ json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp change_date,
bool enabled);
diff --git a/src/extensions/age_restriction/age_restriction.c b/src/extensions/age_restriction/age_restriction.c
index 2d6d0cbd7..481cb133d 100644
--- a/src/extensions/age_restriction/age_restriction.c
+++ b/src/extensions/age_restriction/age_restriction.c
@@ -207,10 +207,10 @@ libtaler_extension_age_restriction_init (void *arg)
return NULL;
}
- mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
+ if (NULL == groups)
+ groups = GNUNET_strdup (TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS);
- if ((groups != NULL) &&
- (GNUNET_OK != TALER_parse_age_group_string (groups, &mask)))
+ if (GNUNET_OK != TALER_parse_age_group_string (groups, &mask))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"[age restriction] couldn't parse age groups: '%s'\n",
diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h
index b5d4dcceb..5ce5e254d 100644
--- a/src/include/taler_bank_service.h
+++ b/src/include/taler_bank_service.h
@@ -111,6 +111,7 @@ struct TALER_BANK_AdminAddIncomingHandle;
* @param timestamp time when the transaction was made.
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON
*/
+// FIXME: bad API
typedef void
(*TALER_BANK_AdminAddIncomingCallback) (
void *cls,
@@ -199,6 +200,7 @@ struct TALER_BANK_TransferHandle;
* @param row_id unique ID of the wire transfer in the bank's records
* @param timestamp when did the transaction go into effect
*/
+// FIXME: bad API
typedef void
(*TALER_BANK_TransferCallback)(
void *cls,
@@ -337,7 +339,7 @@ struct TALER_BANK_CreditHistoryResponse
*/
unsigned int details_length;
- } success;
+ } ok;
} details;
@@ -493,7 +495,7 @@ struct TALER_BANK_DebitHistoryResponse
*/
unsigned int details_length;
- } success;
+ } ok;
} details;
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index 61d92483f..166440764 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -5512,6 +5512,9 @@ TALER_exchange_offline_global_fee_verify (
* Create wire account addition signature.
*
* @param payto_uri bank account
+ * @param conversion_url URL of the conversion service, or NULL if none
+ * @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
+ * @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
* @param now timestamp to use for the signature (rounded)
* @param master_priv private key to sign with
* @param[out] master_sig where to write the signature
@@ -5519,6 +5522,9 @@ TALER_exchange_offline_global_fee_verify (
void
TALER_exchange_offline_wire_add_sign (
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp now,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig);
@@ -5528,6 +5534,9 @@ TALER_exchange_offline_wire_add_sign (
* Verify wire account addition signature.
*
* @param payto_uri bank account
+ * @param conversion_url URL of the conversion service, or NULL if none
+ * @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
+ * @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
* @param sign_time timestamp when signature was created
* @param master_pub public key to verify against
* @param master_sig the signature the signature
@@ -5536,6 +5545,9 @@ TALER_exchange_offline_wire_add_sign (
enum GNUNET_GenericReturnValue
TALER_exchange_offline_wire_add_verify (
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp sign_time,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig);
@@ -5578,6 +5590,9 @@ TALER_exchange_offline_wire_del_verify (
* Check the signature in @a master_sig.
*
* @param payto_uri URI that is signed
+ * @param conversion_url URL of the conversion service, or NULL if none
+ * @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
+ * @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
* @param master_pub master public key of the exchange
* @param master_sig signature of the exchange
* @return #GNUNET_OK if signature is valid
@@ -5585,6 +5600,9 @@ TALER_exchange_offline_wire_del_verify (
enum GNUNET_GenericReturnValue
TALER_exchange_wire_signature_check (
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig);
@@ -5593,12 +5611,18 @@ TALER_exchange_wire_signature_check (
* Create a signed wire statement for the given account.
*
* @param payto_uri account specification
+ * @param conversion_url URL of the conversion service, or NULL if none
+ * @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
+ * @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
* @param master_priv private key to sign with
* @param[out] master_sig where to write the signature
*/
void
TALER_exchange_wire_signature_make (
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig);
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index 4099d6bb0..3769315e8 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -462,21 +462,52 @@ struct TALER_EXCHANGE_HttpResponse
/**
+ * Response from /keys.
+ */
+struct TALER_EXCHANGE_KeysResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Details depending on the HTTP status code.
+ */
+ union
+ {
+
+ /**
+ * Details on #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * Information about the various keys used by the exchange.
+ */
+ const struct TALER_EXCHANGE_Keys *keys;
+
+ /**
+ * Protocol compatibility information
+ */
+ enum TALER_EXCHANGE_VersionCompatibility compat;
+ } ok;
+ } details;
+
+};
+
+
+/**
* Function called with information about who is auditing
* a particular exchange and what keys the exchange is using.
*
* @param cls closure
- * @param hr HTTP response data
- * @param keys information about the various keys used
- * by the exchange, NULL if /keys failed
- * @param compat protocol compatibility information
+ * @param kr response from /keys
*/
typedef void
(*TALER_EXCHANGE_CertificationCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_Keys *keys,
- enum TALER_EXCHANGE_VersionCompatibility compat);
+ const struct TALER_EXCHANGE_KeysResponse *kr);
/**
@@ -712,7 +743,7 @@ TALER_EXCHANGE_get_signing_key_info (
/**
- * Sorted list of fees to be paid for aggregate wire transfers.
+ * List sorted by @a start_date with fees to be paid for aggregate wire transfers.
*/
struct TALER_EXCHANGE_WireAggregateFees
{
@@ -744,6 +775,95 @@ struct TALER_EXCHANGE_WireAggregateFees
/**
+ * Information about wire fees by wire method.
+ */
+struct TALER_EXCHANGE_WireFeesByMethod
+{
+ /**
+ * Wire method with the given @e fees.
+ */
+ const char *method;
+
+ /**
+ * Linked list of wire fees the exchange charges for
+ * accounts of the wire @e method.
+ */
+ struct TALER_EXCHANGE_WireAggregateFees *fees_head;
+
+};
+
+
+/**
+ * Type of an account restriction.
+ */
+enum TALER_EXCHANGE_AccountRestrictionType
+{
+ /**
+ * Invalid restriction.
+ */
+ TALER_EXCHANGE_AR_INVALID = 0,
+
+ /**
+ * Account must not be used for this operation.
+ */
+ TALER_EXCHANGE_AR_DENY = 1,
+
+ /**
+ * Other account must match given regular expression.
+ */
+ TALER_EXCHANGE_AR_REGEX = 2
+};
+
+/**
+ * Restrictions that apply to using a given exchange bank account.
+ */
+struct TALER_EXCHANGE_AccountRestriction
+{
+
+ /**
+ * Type of the account restriction.
+ */
+ enum TALER_EXCHANGE_AccountRestrictionType type;
+
+ /**
+ * Restriction details depending on @e type.
+ */
+ union
+ {
+ /**
+ * Details if type is #TALER_EXCHANGE_AR_REGEX.
+ */
+ struct
+ {
+ /**
+ * Regular expression that the payto://-URI of the partner account must
+ * follow. The regular expression should follow posix-egrep, but
+ * without support for character classes, GNU extensions,
+ * back-references or intervals. See
+ * https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002degrep-regular-expression-syntax.html
+ * for a description of the posix-egrep syntax. Applications may support
+ * regexes with additional features, but exchanges must not use such
+ * regexes.
+ */
+ const char *posix_egrep;
+
+ /**
+ * Hint for a human to understand the restriction.
+ */
+ const char *human_hint;
+
+ /**
+ * Internationalizations for the @e human_hint. Map from IETF BCP 47
+ * language tax to localized human hints.
+ */
+ const json_t *human_hint_i18n;
+ } regex;
+ } details;
+
+};
+
+
+/**
* Information about a wire account of the exchange.
*/
struct TALER_EXCHANGE_WireAccount
@@ -754,37 +874,130 @@ struct TALER_EXCHANGE_WireAccount
const char *payto_uri;
/**
+ * URL of a conversion service in case using this account is subject to
+ * currency conversion. NULL for no conversion needed.
+ */
+ const char *conversion_url;
+
+ /**
+ * Array of restrictions that apply when crediting
+ * this account.
+ */
+ struct TALER_EXCHANGE_AccountRestriction *credit_restrictions;
+
+ /**
+ * Array of restrictions that apply when debiting
+ * this account.
+ */
+ struct TALER_EXCHANGE_AccountRestriction *debit_restrictions;
+
+ /**
+ * Length of the @e credit_restrictions array.
+ */
+ unsigned int credit_restrictions_length;
+
+ /**
+ * Length of the @e debit_restrictions array.
+ */
+ unsigned int debit_restrictions_length;
+
+ /**
* Signature of the exchange over the account (was checked by the API).
*/
struct TALER_MasterSignatureP master_sig;
+};
+
+
+/**
+ * Parse array of @a accounts of the exchange into @a was.
+ *
+ * @param master_pub master public key of the exchange, NULL to not verify signatures
+ * @param accounts array of accounts to parse
+ * @param[out] was where to write the result (already allocated)
+ * @param was_length length of the @a was array, must match the length of @a accounts
+ * @return #GNUNET_OK if parsing @a accounts succeeded
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_parse_accounts (const struct TALER_MasterPublicKeyP *master_pub,
+ const json_t *accounts,
+ struct TALER_EXCHANGE_WireAccount was[],
+ unsigned int was_length);
+
+
+/**
+ * Free data within @a was, but not @a was itself.
+ *
+ * @param was array of wire account data
+ * @param was_len length of the @a was array
+ */
+void
+TALER_EXCHANGE_free_accounts (struct TALER_EXCHANGE_WireAccount *was,
+ unsigned int was_len);
+
+
+/**
+ * Response to a /wire request.
+ */
+struct TALER_EXCHANGE_WireResponse
+{
+ /**
+ * HTTP response details.
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
/**
- * Linked list of wire fees the exchange charges for
- * accounts of the wire method matching @e payto_uri.
+ * Response details depending on status.
*/
- const struct TALER_EXCHANGE_WireAggregateFees *fees;
+ union
+ {
+
+ /**
+ * Details for #MHD_HTTP_OK.
+ */
+ struct
+ {
+
+ /**
+ * Array of accounts of the exchange.
+ */
+ const struct TALER_EXCHANGE_WireAccount *accounts;
+
+ /**
+ * Array of wire fees by wire method.
+ */
+ const struct TALER_EXCHANGE_WireFeesByMethod *fees;
+
+ /**
+ * Length of @e accounts array.
+ */
+ unsigned int accounts_len;
+
+ /**
+ * Length of @e fees array.
+ */
+ unsigned int fees_len;
+ } ok;
+
+ } details;
};
/**
- * Callbacks of this type are used to serve the result of submitting a
- * wire format inquiry request to a exchange.
+ * Callbacks of this type are used to serve the result of submitting a wire
+ * format inquiry request to a exchange.
*
* If the request fails to generate a valid response from the
- * exchange, @a http_status will also be zero.
+ * exchange, the http_status will also be zero.
*
* @param cls closure
- * @param hr HTTP response data
- * @param accounts_len length of the @a accounts array
- * @param accounts list of wire accounts of the exchange, NULL on error
+ * @param wr response data
*/
typedef void
(*TALER_EXCHANGE_WireCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- unsigned int accounts_len,
- const struct TALER_EXCHANGE_WireAccount *accounts);
+ const struct TALER_EXCHANGE_WireResponse *wr);
/**
@@ -976,7 +1189,7 @@ struct TALER_EXCHANGE_DepositResult
*/
const char *transaction_base_url;
- } success;
+ } ok;
/**
* Information returned if the HTTP status is
@@ -1110,7 +1323,7 @@ struct TALER_EXCHANGE_BatchDepositResult
*/
unsigned int num_signatures;
- } success;
+ } ok;
/**
* Information returned if the HTTP status is
@@ -1202,23 +1415,51 @@ TALER_EXCHANGE_batch_deposit_cancel (
*/
struct TALER_EXCHANGE_RefundHandle;
+/**
+ * Response from the /refund API.
+ */
+struct TALER_EXCHANGE_RefundResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Response details depending on the HTTP status code.
+ */
+ union
+ {
+ /**
+ * Details on #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * Exchange key used to sign.
+ */
+ struct TALER_ExchangePublicKeyP exchange_pub;
+
+ /**
+ * The actual signature
+ */
+ struct TALER_ExchangeSignatureP exchange_sig;
+ } ok;
+ } details;
+};
+
/**
* Callbacks of this type are used to serve the result of submitting a
* refund request to an exchange.
*
* @param cls closure
- * @param hr HTTP response data
- * @param sign_key exchange key used to sign @a obj, or NULL
- * @param signature the actual signature, or NULL on error
+ * @param rr refund response
*/
typedef void
(*TALER_EXCHANGE_RefundCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_ExchangePublicKeyP *sign_key,
- const struct TALER_ExchangeSignatureP *signature);
-
+ const struct TALER_EXCHANGE_RefundResponse *rr);
/**
* Submit a refund request to the exchange and get the exchange's response.
@@ -1311,7 +1552,7 @@ struct TALER_EXCHANGE_CsRMeltResponse
* respective coin's withdraw operation.
*/
const struct TALER_ExchangeWithdrawValues *alg_values;
- } success;
+ } ok;
/**
* Details if the status is #MHD_HTTP_GONE.
@@ -1423,7 +1664,7 @@ struct TALER_EXCHANGE_CsRWithdrawResponse
* respective coin's withdraw operation.
*/
struct TALER_ExchangeWithdrawValues alg_values;
- } success;
+ } ok;
/**
* Details if the status is #MHD_HTTP_GONE.
@@ -2209,7 +2450,7 @@ struct TALER_EXCHANGE_WithdrawResponse
/**
* Details if the status is #MHD_HTTP_OK.
*/
- struct TALER_EXCHANGE_PrivateCoinDetails success;
+ struct TALER_EXCHANGE_PrivateCoinDetails ok;
/**
* Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
@@ -2336,7 +2577,7 @@ struct TALER_EXCHANGE_BatchWithdrawResponse
* Length of the @e coins array.
*/
unsigned int num_coins;
- } success;
+ } ok;
/**
* Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
@@ -2431,18 +2672,45 @@ TALER_EXCHANGE_batch_withdraw_cancel (
/**
+ * Response from a withdraw2 request.
+ */
+struct TALER_EXCHANGE_Withdraw2Response
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Response details depending on the HTTP status.
+ */
+ union
+ {
+ /**
+ * Details if HTTP status is #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * blind signature over the coin
+ */
+ struct TALER_BlindedDenominationSignature blind_sig;
+ } ok;
+ } details;
+
+};
+
+/**
* Callbacks of this type are used to serve the result of submitting a
* withdraw request to a exchange without the (un)blinding factor.
*
* @param cls closure
- * @param hr HTTP response data
- * @param blind_sig blind signature over the coin, NULL on error
+ * @param w2r response data
*/
typedef void
(*TALER_EXCHANGE_Withdraw2Callback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_BlindedDenominationSignature *blind_sig);
+ const struct TALER_EXCHANGE_Withdraw2Response *w2r);
/**
@@ -2493,20 +2761,52 @@ TALER_EXCHANGE_withdraw2_cancel (struct TALER_EXCHANGE_Withdraw2Handle *wh);
/**
+ * Response from a batch-withdraw request (2nd variant).
+ */
+struct TALER_EXCHANGE_BatchWithdraw2Response
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Response details depending on the HTTP status.
+ */
+ union
+ {
+ /**
+ * Details if HTTP status is #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * array of blind signatures over the coins.
+ */
+ const struct TALER_BlindedDenominationSignature *blind_sigs;
+
+ /**
+ * length of @e blind_sigs
+ */
+ unsigned int blind_sigs_length;
+
+ } ok;
+ } details;
+
+};
+
+
+/**
* Callbacks of this type are used to serve the result of submitting a batch
* withdraw request to a exchange without the (un)blinding factor.
*
* @param cls closure
- * @param hr HTTP response data
- * @param blind_sigs array of blind signatures over the coins, NULL on error
- * @param blind_sigs_length length of @a blind_sigs
+ * @param bw2r response data
*/
typedef void
(*TALER_EXCHANGE_BatchWithdraw2Callback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_BlindedDenominationSignature *blind_sigs,
- unsigned int blind_sigs_length);
+ const struct TALER_EXCHANGE_BatchWithdraw2Response *bw2r);
/**
@@ -2675,7 +2975,7 @@ struct TALER_EXCHANGE_MeltResponse
* Gamma value chosen by the exchange.
*/
uint32_t noreveal_index;
- } success;
+ } ok;
} details;
};
@@ -2801,7 +3101,7 @@ struct TALER_EXCHANGE_RevealResult
* Number of coins returned.
*/
unsigned int num_coins;
- } success;
+ } ok;
} details;
@@ -2947,7 +3247,7 @@ struct TALER_EXCHANGE_LinkResult
* Number of coins returned.
*/
unsigned int num_coins;
- } success;
+ } ok;
} details;
@@ -3060,18 +3360,43 @@ struct TALER_EXCHANGE_TransferData
/**
+ * Response for a GET /transfers request.
+ */
+struct TALER_EXCHANGE_TransfersGetResponse
+{
+ /**
+ * HTTP response.
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Details depending on HTTP status code.
+ */
+ union
+ {
+ /**
+ * Details if status code is #MHD_HTTP_OK.
+ */
+ struct
+ {
+ struct TALER_EXCHANGE_TransferData td;
+ } ok;
+
+ } details;
+};
+
+
+/**
* Function called with detailed wire transfer data, including all
* of the coin transactions that were combined into the wire transfer.
*
* @param cls closure
- * @param hr HTTP response data
- * @param ta transfer data, (set only if @a http_status is #MHD_HTTP_OK, otherwise NULL)
+ * @param tgr response data
*/
typedef void
(*TALER_EXCHANGE_TransfersGetCallback)(
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_TransferData *ta);
+ const struct TALER_EXCHANGE_TransfersGetResponse *tgr);
/**
@@ -3163,7 +3488,7 @@ struct TALER_EXCHANGE_GetDepositResponse
*/
struct TALER_Amount coin_contribution;
- } success;
+ } ok;
/**
* Response if the status was #MHD_HTTP_ACCEPTED
@@ -3221,6 +3546,7 @@ typedef void
* @param h_wire hash of merchant's wire transfer details
* @param h_contract_terms hash of the proposal data
* @param coin_pub public key of the coin
+ * @param timeout timeout to use for long-polling, 0 for no long polling
* @param cb function to call with the result
* @param cb_cls closure for @a cb
* @return handle to abort request
@@ -3232,6 +3558,7 @@ TALER_EXCHANGE_deposits_get (
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct GNUNET_TIME_Relative timeout,
TALER_EXCHANGE_DepositGetCallback cb,
void *cb_cls);
@@ -3316,20 +3643,49 @@ struct TALER_EXCHANGE_RecoupHandle;
/**
+ * Response from a recoup request.
+ */
+struct TALER_EXCHANGE_RecoupResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Response details depending on the HTTP status.
+ */
+ union
+ {
+ /**
+ * Details if HTTP status is #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * public key of the reserve receiving the recoup
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ } ok;
+ } details;
+
+};
+
+
+/**
* Callbacks of this type are used to return the final result of
* submitting a recoup request to a exchange. If the operation was
* successful, this function returns the @a reserve_pub of the
* reserve that was credited.
*
* @param cls closure
- * @param hr HTTP response data
- * @param reserve_pub public key of the reserve receiving the recoup
+ * @param rr response data
*/
typedef void
(*TALER_EXCHANGE_RecoupResultCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_ReservePublicKeyP *reserve_pub);
+ const struct TALER_EXCHANGE_RecoupResponse *rr);
/**
@@ -3378,18 +3734,47 @@ struct TALER_EXCHANGE_RecoupRefreshHandle;
/**
+ * Response from a /recoup-refresh request.
+ */
+struct TALER_EXCHANGE_RecoupRefreshResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Response details depending on the HTTP status.
+ */
+ union
+ {
+ /**
+ * Details if HTTP status is #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * public key of the dirty coin that was credited
+ */
+ struct TALER_CoinSpendPublicKeyP old_coin_pub;
+
+ } ok;
+ } details;
+
+};
+
+
+/**
* Callbacks of this type are used to return the final result of
* submitting a recoup-refresh request to a exchange.
*
* @param cls closure
- * @param hr HTTP response data
- * @param old_coin_pub public key of the dirty coin that was credited
+ * @param rrr response data
*/
typedef void
(*TALER_EXCHANGE_RecoupRefreshResultCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_CoinSpendPublicKeyP *old_coin_pub);
+ const struct TALER_EXCHANGE_RecoupRefreshResponse *rrr);
/**
@@ -3495,7 +3880,7 @@ struct TALER_EXCHANGE_KycStatus
*/
enum TALER_AmlDecisionState aml_status;
- } success;
+ } ok;
/**
* KYC is required.
@@ -3899,18 +4284,47 @@ struct TALER_EXCHANGE_FutureKeys
/**
+ * Response from a /management/keys request.
+ */
+struct TALER_EXCHANGE_ManagementGetKeysResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Response details depending on the HTTP status.
+ */
+ union
+ {
+ /**
+ * Details if HTTP status is #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * information about the various keys used
+ * by the exchange
+ */
+ struct TALER_EXCHANGE_FutureKeys keys;
+
+ } ok;
+ } details;
+
+};
+
+
+/**
* Function called with information about future keys.
*
* @param cls closure
- * @param hr HTTP response data
- * @param keys information about the various keys used
- * by the exchange, NULL if /management/keys failed
+ * @param mgr HTTP response data
*/
typedef void
(*TALER_EXCHANGE_ManagementGetKeysCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_FutureKeys *keys);
+ const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr);
/**
@@ -4013,15 +4427,28 @@ struct TALER_EXCHANGE_ManagementPostKeysData
/**
+ * Response from a POST /management/keys request.
+ */
+struct TALER_EXCHANGE_ManagementPostKeysResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+};
+
+
+/**
* Function called with information about the post keys operation result.
*
* @param cls closure
- * @param hr HTTP response data
+ * @param mr response data
*/
typedef void
(*TALER_EXCHANGE_ManagementPostKeysCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr);
+ const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr);
/**
@@ -4071,6 +4498,20 @@ struct TALER_EXCHANGE_ManagementPostExtensionsData
struct TALER_MasterSignatureP extensions_sig;
};
+
+/**
+ * Response from a POST /management/extensions request.
+ */
+struct TALER_EXCHANGE_ManagementPostExtensionsResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+};
+
+
/**
* Function called with information about the post extensions operation result.
*
@@ -4080,7 +4521,7 @@ struct TALER_EXCHANGE_ManagementPostExtensionsData
typedef void
(*TALER_EXCHANGE_ManagementPostExtensionsCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr);
+ const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *hr);
/**
* @brief Handle for a POST /management/extensions request.
@@ -4119,6 +4560,19 @@ TALER_EXCHANGE_management_post_extensions_cancel (
/**
+ * Response from a POST /management/drain request.
+ */
+struct TALER_EXCHANGE_ManagementDrainResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+};
+
+
+/**
* Function called with information about the drain profits result.
*
* @param cls closure
@@ -4127,7 +4581,7 @@ TALER_EXCHANGE_management_post_extensions_cancel (
typedef void
(*TALER_EXCHANGE_ManagementDrainProfitsCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr);
+ const struct TALER_EXCHANGE_ManagementDrainResponse *hr);
/**
@@ -4176,6 +4630,19 @@ TALER_EXCHANGE_management_drain_profits_cancel (
/**
+ * Response from a POST /management/denominations/$DENOM/revoke request.
+ */
+struct TALER_EXCHANGE_ManagementRevokeDenominationResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+};
+
+
+/**
* Function called with information about the post revocation operation result.
*
* @param cls closure
@@ -4184,7 +4651,7 @@ TALER_EXCHANGE_management_drain_profits_cancel (
typedef void
(*TALER_EXCHANGE_ManagementRevokeDenominationKeyCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr);
+ const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *hr);
/**
@@ -4225,6 +4692,18 @@ TALER_EXCHANGE_management_revoke_denomination_key_cancel (
/**
+ * Response from a POST /management/signkeys/$SK/revoke request.
+ */
+struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+};
+
+/**
* Function called with information about the post revocation operation result.
*
* @param cls closure
@@ -4233,7 +4712,7 @@ TALER_EXCHANGE_management_revoke_denomination_key_cancel (
typedef void
(*TALER_EXCHANGE_ManagementRevokeSigningKeyCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr);
+ const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *hr);
/**
@@ -4274,6 +4753,18 @@ TALER_EXCHANGE_management_revoke_signing_key_cancel (
/**
+ * Response from a POST /management/aml-officers request.
+ */
+struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse
+{
+ /**
+ * HTTP response data
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+};
+
+/**
* Function called with information about the change to
* an AML officer status.
*
@@ -4283,7 +4774,7 @@ TALER_EXCHANGE_management_revoke_signing_key_cancel (
typedef void
(*TALER_EXCHANGE_ManagementUpdateAmlOfficerCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr);
+ const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *hr);
/**
@@ -4390,7 +4881,7 @@ struct TALER_EXCHANGE_AmlDecisionsResponse
*/
unsigned int decisions_length;
- } success;
+ } ok;
} details;
};
@@ -4547,7 +5038,7 @@ struct TALER_EXCHANGE_AmlDecisionResponse
*/
unsigned int kyc_attributes_length;
- } success;
+ } ok;
} details;
};
@@ -4617,6 +5108,7 @@ struct TALER_EXCHANGE_AddAmlDecision;
* @param cls closure
* @param hr HTTP response data
*/
+// FIXME: bad API
typedef void
(*TALER_EXCHANGE_AddAmlDecisionCallback) (
void *cls,
@@ -4672,6 +5164,7 @@ TALER_EXCHANGE_add_aml_decision_cancel (
* @param cls closure
* @param hr HTTP response data
*/
+// FIXME: bad API
typedef void
(*TALER_EXCHANGE_ManagementAddPartnerCallback) (
void *cls,
@@ -4732,6 +5225,7 @@ TALER_EXCHANGE_management_add_partner_cancel (
* @param cls closure
* @param hr HTTP response data
*/
+// FIXME: bad API
typedef void
(*TALER_EXCHANGE_ManagementAuditorEnableCallback) (
void *cls,
@@ -4787,6 +5281,7 @@ TALER_EXCHANGE_management_enable_auditor_cancel (
* @param cls closure
* @param hr HTTP response data
*/
+// FIXME: bad API
typedef void
(*TALER_EXCHANGE_ManagementAuditorDisableCallback) (
void *cls,
@@ -4833,15 +5328,27 @@ TALER_EXCHANGE_management_disable_auditor_cancel (
/**
+ * Response from an exchange account/enable operation.
+ */
+struct TALER_EXCHANGE_ManagementWireEnableResponse
+{
+ /**
+ * HTTP response data.
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+};
+
+
+/**
* Function called with information about the wire enable operation result.
*
* @param cls closure
- * @param hr HTTP response data
+ * @param wer HTTP response data
*/
typedef void
(*TALER_EXCHANGE_ManagementWireEnableCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr);
+ const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer);
/**
@@ -4856,6 +5363,9 @@ struct TALER_EXCHANGE_ManagementWireEnableHandle;
* @param ctx the context
* @param url HTTP base URL for the exchange
* @param payto_uri RFC 8905 URI of the exchange's bank account
+ * @param conversion_url URL of the conversion service, or NULL if none
+ * @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
+ * @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
* @param validity_start when was this decided?
* @param master_sig1 signature affirming the wire addition
* of purpose #TALER_SIGNATURE_MASTER_ADD_WIRE
@@ -4870,6 +5380,9 @@ TALER_EXCHANGE_management_enable_wire (
struct GNUNET_CURL_Context *ctx,
const char *url,
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp validity_start,
const struct TALER_MasterSignatureP *master_sig1,
const struct TALER_MasterSignatureP *master_sig2,
@@ -4888,15 +5401,26 @@ TALER_EXCHANGE_management_enable_wire_cancel (
/**
+ * Response from an exchange account/disable operation.
+ */
+struct TALER_EXCHANGE_ManagementWireDisableResponse
+{
+ /**
+ * HTTP response data.
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+};
+
+/**
* Function called with information about the wire disable operation result.
*
* @param cls closure
- * @param hr HTTP response data
+ * @param wdr response data
*/
typedef void
(*TALER_EXCHANGE_ManagementWireDisableCallback) (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr);
+ const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdr);
/**
@@ -4945,6 +5469,7 @@ TALER_EXCHANGE_management_disable_wire_cancel (
* @param cls closure
* @param hr HTTP response data
*/
+// FIXME: bad API
typedef void
(*TALER_EXCHANGE_ManagementSetWireFeeCallback) (
void *cls,
@@ -5001,6 +5526,7 @@ TALER_EXCHANGE_management_set_wire_fees_cancel (
* @param cls closure
* @param hr HTTP response data
*/
+// FIXME: bad API
typedef void
(*TALER_EXCHANGE_ManagementSetGlobalFeeCallback) (
void *cls,
@@ -5062,6 +5588,7 @@ TALER_EXCHANGE_management_set_global_fees_cancel (
* @param cls closure
* @param hr HTTP response data
*/
+// FIXME: bad API
typedef void
(*TALER_EXCHANGE_AuditorAddDenominationCallback) (
void *cls,
@@ -5147,7 +5674,7 @@ struct TALER_EXCHANGE_ContractGetResponse
*/
size_t econtract_size;
- } success;
+ } ok;
} details;
@@ -5243,7 +5770,7 @@ struct TALER_EXCHANGE_PurseGetResponse
*/
struct GNUNET_TIME_Timestamp purse_expiration;
- } success;
+ } ok;
} details;
@@ -5333,7 +5860,7 @@ struct TALER_EXCHANGE_PurseCreateDepositResponse
struct TALER_ExchangeSignatureP exchange_sig;
- } success;
+ } ok;
} details;
@@ -5532,7 +6059,7 @@ struct TALER_EXCHANGE_AccountMergeResponse
*/
struct GNUNET_TIME_Timestamp etime;
- } success;
+ } ok;
/**
* Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
@@ -5640,7 +6167,7 @@ struct TALER_EXCHANGE_PurseCreateMergeResponse
struct
{
- } success;
+ } ok;
/**
* Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
@@ -5760,7 +6287,7 @@ struct TALER_EXCHANGE_PurseDepositResponse
*/
struct TALER_PrivateContractHashP h_contract_terms;
- } success;
+ } ok;
} details;
};
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 43f6b73e8..3a6ba6514 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -149,7 +149,25 @@ struct TALER_EXCHANGEDB_DenominationKeyInformation
GNUNET_NETWORK_STRUCT_BEGIN
/**
- * Signature of events signalling a reserve got funding.
+ * Events signalling that a coin deposit status
+ * changed.
+ */
+struct TALER_CoinDepositEventP
+{
+ /**
+ * Of type #TALER_DBEVENT_EXCHANGE_DEPOSIT_STATUS_CHANGED.
+ */
+ struct GNUNET_DB_EventHeaderP header;
+
+ /**
+ * Public key of the merchant.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+};
+
+/**
+ * Events signalling a reserve got funding.
*/
struct TALER_ReserveEventP
{
@@ -760,7 +778,7 @@ struct TALER_EXCHANGEDB_TableData
struct TALER_ReserveSignatureP reserve_sig;
uint32_t noreveal_index;
struct GNUNET_TIME_Absolute timestamp;
- } withdraw_age_commitments;
+ } age_withdraw_commitments;
struct
{
@@ -769,10 +787,9 @@ struct TALER_EXCHANGEDB_TableData
uint64_t denominations_serial;
void *coin_ev;
size_t coin_ev_size;
- struct TALER_ExchangeWithdrawValues ewv;
// h_coin_ev omitted, to be recomputed!
struct TALER_BlindedDenominationSignature ev_sig;
- } withdraw_age_revealed_coins;
+ } age_withdraw_revealed_coins;
} details;
@@ -1200,8 +1217,9 @@ struct TALER_EXCHANGEDB_AgeWithdrawCommitment
struct TALER_ReservePublicKeyP reserve_pub;
/**
- * Signature confirming the age withdrawal, matching @e reserve_pub, @e
- * maximum_age_group and @e h_commitment and @e total_amount_with_fee.
+ * Signature confirming the age withdrawal commitment, matching @e
+ * reserve_pub, @e maximum_age_group and @e h_commitment and @e
+ * total_amount_with_fee.
*/
struct TALER_ReserveSignatureP reserve_sig;
@@ -2737,6 +2755,28 @@ struct TALER_EXCHANGEDB_CsRevealFreshCoinData
uint32_t coin_off;
};
+/**
+ * Information about a coin that was revealed to the exchange
+ * during reveal.
+ */
+struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin
+{
+ /**
+ * Hash of the public denomination key of the coin.
+ */
+ struct TALER_DenominationHashP h_denom_pub;
+
+ /**
+ * Signature generated by the exchange over the coin (in blinded format).
+ */
+ struct TALER_BlindedDenominationSignature coin_sig;
+
+ /**
+ * Blinded hash of the new coin
+ */
+ struct TALER_BlindedCoinHashP h_coin_ev;
+};
+
/**
* Generic KYC status for some operation.
@@ -2859,6 +2899,9 @@ typedef enum GNUNET_GenericReturnValue
*
* @param cls closure
* @param payto_uri the exchange bank account URI
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account
+ * @param credit_restrictions JSON array with credit restrictions on the account
* @param master_sig master key signature affirming that this is a bank
* account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
*/
@@ -2866,6 +2909,9 @@ typedef void
(*TALER_EXCHANGEDB_WireAccountCallback)(
void *cls,
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
const struct TALER_MasterSignatureP *master_sig);
@@ -3810,19 +3856,18 @@ struct TALER_EXCHANGEDB_Plugin
* age restriction enabled in a given age-withdraw operation and the relevant
* information we learned or created in the reveal steop
*
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param h_commitment The hash of the original age-withdraw commitment, which is a key into the withdraw_age_commitments table
- * @param num_coins number of coins to generate, size of the @a coin_evs array
- * TODO: oec
+ * @param cls The `struct PostgresClosure` with the plugin-specific state
+ * @param h_commitment The hash of the original age-withdraw commitment, which is a key into the age_withdraw_commitments table
+ * @param num_awrcs Number of coins to generate, size of the @a coin_evs array
+ * @param awrcs Array of @a num_awrcs information about coins to be created
* @return query execution status
*/
enum GNUNET_DB_QueryStatus
(*insert_age_withdraw_reveal)(
void *cls,
- uint64_t h_commitment,
- uint32_t num_coins
- /* TODO: oec */
- );
+ const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
+ uint32_t num_awrcs,
+ const struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin *awrcs);
/**
* Lookup in the database for the fresh coins with age-restriction that
@@ -5523,6 +5568,9 @@ struct TALER_EXCHANGEDB_Plugin
*
* @param cls closure
* @param payto_uri wire account of the exchange
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account
+ * @param credit_restrictions JSON array with credit restrictions on the account
* @param start_date date when the account was added by the offline system
* (only to be used for replay detection)
* @param master_sig public signature affirming the existence of the account,
@@ -5532,6 +5580,9 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus
(*insert_wire)(void *cls,
const char *payto_uri,
+ const char *conversion_url,
+ json_t *debit_restrictions,
+ json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp start_date,
const struct TALER_MasterSignatureP *master_sig);
@@ -5541,6 +5592,9 @@ struct TALER_EXCHANGEDB_Plugin
*
* @param cls closure
* @param payto_uri account the update is about
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account; NULL allowed if not @a enabled
+ * @param credit_restrictions JSON array with credit restrictions on the account; NULL allowed if not @a enabled
* @param change_date date when the account status was last changed
* (only to be used for replay detection)
* @param enabled true to enable, false to disable (the actual change)
@@ -5549,6 +5603,9 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus
(*update_wire)(void *cls,
const char *payto_uri,
+ const char *conversion_url,
+ json_t *debit_restrictions,
+ json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp change_date,
bool enabled);
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index bd5b72480..75f225344 100644
--- a/src/include/taler_extensions.h
+++ b/src/include/taler_extensions.h
@@ -344,10 +344,6 @@ TALER_extensions_verify_manifests_signature (
* The default age mask represents the age groups
* 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-20, 21-...
*/
-#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 \
- | 1 << 12 | 1 << 14 \
- | 1 << 16 | 1 << 18 \
- | 1 << 21)
#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 7f17df030..d0749808d 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -675,33 +675,6 @@ TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s,
/**
- * Check the signature in @a wire_s. Also performs rudimentary
- * checks on the account data *if* supported.
- *
- * @param wire_s signed wire information of an exchange
- * @param master_pub master public key of the exchange
- * @return #GNUNET_OK if signature is valid
- */
-enum GNUNET_GenericReturnValue
-TALER_JSON_exchange_wire_signature_check (
- const json_t *wire_s,
- const struct TALER_MasterPublicKeyP *master_pub);
-
-
-/**
- * Create a signed wire statement for the given account.
- *
- * @param payto_uri account specification
- * @param master_priv private key to sign with
- * @return NULL if @a payto_uri is malformed
- */
-json_t *
-TALER_JSON_exchange_wire_signature_make (
- const char *payto_uri,
- const struct TALER_MasterPrivateKeyP *master_priv);
-
-
-/**
* Extract a string from @a object under the field @a field, but respecting
* the Taler i18n rules and the language preferences expressed in @a
* language_pattern.
diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h
index b64231352..e4aa916e7 100644
--- a/src/include/taler_mhd_lib.h
+++ b/src/include/taler_mhd_lib.h
@@ -437,7 +437,47 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection,
/**
- * Extract fixed-size base32crockford encoded data from request.
+ * Extract optional "timeout_ms" argument from request.
+ *
+ * @param connection the MHD connection
+ * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout,
+ * the current time plus the value given under "timeout_ms" otherwise
+ * @return #GNUNET_OK on success, #GNUNET_NO if an
+ * error was returned on @a connection (caller should return #MHD_YES) and
+ * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO)
+ */
+enum GNUNET_GenericReturnValue
+TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection,
+ struct GNUNET_TIME_Absolute *expiration);
+
+
+/**
+ * Extract optional "timeout_ms" argument from request.
+ * Macro that *returns* #MHD_YES/#MHD_NO if the "timeout_ms"
+ * argument existed but failed to parse.
+ *
+ * @param connection the MHD connection
+ * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout,
+ * the current time plus the value given under "timeout_ms" otherwise
+ */
+#define TALER_MHD_parse_request_timeout(connection,expiration) \
+ do { \
+ switch (TALER_MHD_parse_request_arg_timeout (connection, \
+ expiration)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ case GNUNET_OK: \
+ break; \
+ } \
+ } while (0)
+
+
+/**
+ * Extract fixed-size base32crockford encoded data from request argument.
*
* Queues an error response to the connection if the parameter is missing or
* invalid.
@@ -446,16 +486,195 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection,
* @param param_name the name of the parameter with the key
* @param[out] out_data pointer to store the result
* @param out_size expected size of @a out_data
+ * @param[out] present set to true if argument was found
* @return
* #GNUNET_YES if the the argument is present
- * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_NO if the argument is malformed
* #GNUNET_SYSERR on internal error (error response could not be sent)
*/
enum GNUNET_GenericReturnValue
TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
const char *param_name,
void *out_data,
- size_t out_size);
+ size_t out_size,
+ bool *present);
+
+
+/**
+ * Extract fixed-size base32crockford encoded data from request header.
+ *
+ * Queues an error response to the connection if the parameter is missing or
+ * invalid.
+ *
+ * @param connection the MHD connection
+ * @param header_name the name of the HTTP header with the value
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of @a out_data
+ * @param[out] present set to true if argument was found
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+enum GNUNET_GenericReturnValue
+TALER_MHD_parse_request_header_data (struct MHD_Connection *connection,
+ const char *header_name,
+ void *out_data,
+ size_t out_size,
+ bool *present);
+
+/**
+ * Extract fixed-size base32crockford encoded data from request.
+ *
+ * @param connection the MHD connection
+ * @param name the name of the parameter with the key
+ * @param[out] val pointer to store the result, type must determine size
+ * @param[in,out] required pass true to require presence of this argument; if 'false'
+ * set to true if the argument was found
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+#define TALER_MHD_parse_request_arg_auto(connection,name,val,required) \
+ do { \
+ bool p; \
+ switch (TALER_MHD_parse_request_arg_data (connection, name, \
+ val, sizeof (*val), &p)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ return MHD_YES; \
+ case GNUNET_OK: \
+ if (required & (! p)) \
+ return TALER_MHD_reply_with_error ( \
+ connection, \
+ MHD_HTTP_BAD_REQUEST, \
+ TALER_EC_GENERIC_PARAMETER_MISSING, \
+ name); \
+ required = p; \
+ break; \
+ } \
+ } while (0)
+
+
+/**
+ * Extract required fixed-size base32crockford encoded data from request.
+ *
+ * @param connection the MHD connection
+ * @param name the name of the parameter with the key
+ * @param[out] val pointer to store the result, type must determine size
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+#define TALER_MHD_parse_request_arg_auto_t(connection,name,val) \
+ do { \
+ bool b = true; \
+ TALER_MHD_parse_request_arg_auto (connection,name,val,b); \
+ } while (0)
+
+/**
+ * Extract fixed-size base32crockford encoded data from request.
+ *
+ * @param connection the MHD connection
+ * @param name the name of the header with the key
+ * @param[out] val pointer to store the result, type must determine size
+ * @param[in,out] required pass true to require presence of this argument; if 'false'
+ * set to true if the argument was found
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+#define TALER_MHD_parse_request_header_auto(connection,name,val,required) \
+ do { \
+ bool p; \
+ switch (TALER_MHD_parse_request_header_data (connection, name, \
+ val, sizeof (*val), &p)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ return MHD_YES; \
+ case GNUNET_OK: \
+ if (required & (! p)) \
+ return TALER_MHD_reply_with_error ( \
+ connection, \
+ MHD_HTTP_BAD_REQUEST, \
+ TALER_EC_GENERIC_PARAMETER_MISSING, \
+ name); \
+ required = p; \
+ break; \
+ } \
+ } while (0)
+
+
+/**
+ * Extract required fixed-size base32crockford encoded data from request.
+ *
+ * @param connection the MHD connection
+ * @param name the name of the header with the key
+ * @param[out] val pointer to store the result, type must determine size
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+#define TALER_MHD_parse_request_header_auto_t(connection,name,val) \
+ do { \
+ bool b = true; \
+ TALER_MHD_parse_request_header_auto (connection,name,val,b); \
+ } while (0)
+
+
+/**
+ * Check that the 'Content-Length' header is giving
+ * a length below @a max_len. If not, return an
+ * appropriate error response and return the
+ * correct #MHD_YES/#MHD_NO value from this function.
+ *
+ * @param connection the MHD connection
+ * @param max_len maximum allowed content length
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+enum GNUNET_GenericReturnValue
+TALER_MHD_check_content_length_ (struct MHD_Connection *connection,
+ unsigned long long max_len);
+
+
+/**
+ * Check that the 'Content-Length' header is giving
+ * a length below @a max_len. If not, return an
+ * appropriate error response and return the
+ * correct #MHD_YES/#MHD_NO value from this function.
+ *
+ * @param connection the MHD connection
+ * @param max_len maximum allowed content length
+ */
+#define TALER_MHD_check_content_length(connection,max_len) \
+ do { \
+ switch (TALER_MHD_check_content_length_ (connection, max_len)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ return MHD_YES; \
+ case GNUNET_OK: \
+ break; \
+ } \
+ } while (0)
/**
diff --git a/src/include/taler_templating_lib.h b/src/include/taler_templating_lib.h
index 53946b9c0..6af6db715 100644
--- a/src/include/taler_templating_lib.h
+++ b/src/include/taler_templating_lib.h
@@ -22,7 +22,7 @@
#define TALER_TEMPLATING_LIB_H
#include <microhttpd.h>
-
+#include "taler_mhd_lib.h"
/**
* Fill in Mustach template @a tmpl using the data from @a root
@@ -91,6 +91,26 @@ TALER_TEMPLATING_reply (struct MHD_Connection *connection,
const char *taler_uri,
const json_t *root);
+
+/**
+ * Load a @a template and substitute an error message based on @a ec and @a
+ * detail, returning the result to the @a connection with the given @a
+ * http_status code.
+ *
+ * @param connection the connection we act upon
+ * @param template_basename basename of the template to load
+ * @param http_status code to use on success
+ * @param ec error code to return
+ * @param detail optional text to add to the template
+ * @return #MHD_YES on success, #MHD_NO to just close the connection
+ */
+MHD_RESULT
+TALER_TEMPLATING_reply_error (struct MHD_Connection *connection,
+ const char *template_basename,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const char *detail);
+
/**
* Preload templates.
*
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 37d347c30..664db6ccd 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -147,15 +147,11 @@ TALER_TESTING_prepare_exchange (const char *config_filename,
*
* @param cls closure, typically, the "run" method containing
* all the commands to be run, and a closure for it.
- * @param hr http response details
- * @param keys the exchange's keys.
- * @param compat protocol compatibility information.
+ * @param kr response details
*/
void
TALER_TESTING_cert_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_Keys *keys,
- enum TALER_EXCHANGE_VersionCompatibility compat);
+ const struct TALER_EXCHANGE_KeysResponse *kr);
/**
@@ -1243,6 +1239,36 @@ TALER_TESTING_cmd_exec_wirewatch (const char *label,
/**
+ * Request URL via "wget".
+ *
+ * @param label command label.
+ * @param url URL to fetch
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_wget (const char *label,
+ const char *url);
+
+
+/**
+ * Request fetch-transactions via "wget".
+ *
+ * @param label command label.
+ * @param username username to use
+ * @param password password to use
+ * @param bank_base_url base URL of the nexus
+ * @param account_id account to fetch transactions for
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_nexus_fetch_transactions (const char *label,
+ const char *username,
+ const char *password,
+ const char *bank_base_url,
+ const char *account_id);
+
+
+/**
* Make a "expire" CMD.
*
* @param label command label.
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 8192ed87c..1de264c12 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
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
@@ -230,6 +230,16 @@ TALER_OS_init (void);
/**
+ * Re-encode string at @a inp to match RFC 8785 (section 3.2.2.2).
+ *
+ * @param[in,out] inp pointer to string to re-encode
+ * @return number of bytes in resulting @a inp
+ */
+size_t
+TALER_rfc8785encode (char **inp);
+
+
+/**
* URL-encode a string according to rfc3986.
*
* @param s string to encode
@@ -559,6 +569,58 @@ enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask);
+
+/**
+ * Handle to an external process that will assist
+ * with some JSON-to-JSON conversion.
+ */
+struct TALER_JSON_ExternalConversion;
+
+/**
+ * Type of a callback that receives a JSON @a result.
+ *
+ * @param cls closure
+ * @param status_type how did the process die
+ * @param code termination status code from the process
+ * @param result some JSON result, NULL if we failed to get an JSON output
+ */
+typedef void
+(*TALER_JSON_JsonCallback) (void *cls,
+ enum GNUNET_OS_ProcessStatusType status_type,
+ unsigned long code,
+ const json_t *result);
+
+
+/**
+ * Launch some external helper @a binary to convert some @a input
+ * and eventually call @a cb with the result.
+ *
+ * @param input JSON to serialize and pass to the helper process
+ * @param cb function to call on the result
+ * @param cb_cls closure for @a cb
+ * @param binary name of the binary to execute
+ * @param ... NULL-terminated list of arguments for the @a binary,
+ * usually starting with again the name of the binary
+ * @return handle to cancel the operation (and kill the helper)
+ */
+struct TALER_JSON_ExternalConversion *
+TALER_JSON_external_conversion_start (const json_t *input,
+ TALER_JSON_JsonCallback cb,
+ void *cb_cls,
+ const char *binary,
+ ...);
+
+/**
+ * Abort external conversion, killing the process and preventing
+ * the callback from being called. Must not be called after the
+ * callback was invoked.
+ *
+ * @param[in] ec external conversion handle to cancel
+ */
+void
+TALER_JSON_external_conversion_stop (
+ struct TALER_JSON_ExternalConversion *ec);
+
#undef __TALER_UTIL_LIB_H_INSIDE__
#endif
diff --git a/src/json/Makefile.am b/src/json/Makefile.am
index 2f5ec3f17..6bd5b464c 100644
--- a/src/json/Makefile.am
+++ b/src/json/Makefile.am
@@ -28,12 +28,10 @@ libtalerjson_la_LIBADD = \
$(XLIB)
TESTS = \
- test_json \
- test_json_wire
+ test_json
check_PROGRAMS= \
- test_json \
- test_json_wire
+ test_json
test_json_SOURCES = \
test_json.c
@@ -43,13 +41,3 @@ test_json_LDADD = \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil \
-ljansson
-
-
-test_json_wire_SOURCES = \
- test_json_wire.c
-test_json_wire_LDADD = \
- $(top_builddir)/src/json/libtalerjson.la \
- -lgnunetjson \
- $(top_builddir)/src/util/libtalerutil.la \
- -lgnunetutil \
- -ljansson
diff --git a/src/json/json.c b/src/json/json.c
index 7d7e4ecba..fb00fb535 100644
--- a/src/json/json.c
+++ b/src/json/json.c
@@ -62,170 +62,6 @@ contains_real (const json_t *json)
/**
- * Dump character in the low range into @a buf
- * following RFC 8785.
- *
- * @param[in,out] buf buffer to modify
- * @param val value to dump
- */
-static void
-lowdump (struct GNUNET_Buffer *buf,
- unsigned char val)
-{
- char scratch[7];
-
- switch (val)
- {
- case 0x8:
- GNUNET_buffer_write (buf,
- "\\b",
- 2);
- break;
- case 0x9:
- GNUNET_buffer_write (buf,
- "\\t",
- 2);
- break;
- case 0xA:
- GNUNET_buffer_write (buf,
- "\\n",
- 2);
- break;
- case 0xC:
- GNUNET_buffer_write (buf,
- "\\f",
- 2);
- break;
- case 0xD:
- GNUNET_buffer_write (buf,
- "\\r",
- 2);
- break;
- default:
- GNUNET_snprintf (scratch,
- sizeof (scratch),
- "\\u%04x",
- (unsigned int) val);
- GNUNET_buffer_write (buf,
- scratch,
- 6);
- break;
- }
-}
-
-
-/**
- * Re-encode string at @a inp to match RFC 8785 (section 3.2.2.2).
- *
- * @param[in,out] inp pointer to string to re-encode
- * @return number of bytes in resulting @a inp
- */
-static size_t
-rfc8785encode (char **inp)
-{
- struct GNUNET_Buffer buf = { 0 };
- size_t left = strlen (*inp) + 1;
- size_t olen;
- char *in = *inp;
- const char *pos = in;
-
- GNUNET_buffer_prealloc (&buf,
- left + 40);
- buf.warn_grow = 0; /* disable, + 40 is just a wild guess */
- while (1)
- {
- int mbl = u8_mblen ((unsigned char *) pos,
- left);
- unsigned char val;
-
- if (0 == mbl)
- break;
- val = (unsigned char) *pos;
- if ( (1 == mbl) &&
- (val <= 0x1F) )
- {
- /* Should not happen, as input is produced by
- * JSON stringification */
- GNUNET_break (0);
- lowdump (&buf,
- val);
- }
- else if ( (1 == mbl) && ('\\' == *pos) )
- {
- switch (*(pos + 1))
- {
- case '\\':
- mbl = 2;
- GNUNET_buffer_write (&buf,
- pos,
- mbl);
- break;
- case 'u':
- {
- unsigned int num;
- uint32_t n32;
- unsigned char res[8];
- size_t rlen;
-
- GNUNET_assert ( (1 ==
- sscanf (pos + 2,
- "%4x",
- &num)) ||
- (1 ==
- sscanf (pos + 2,
- "%4X",
- &num)) );
- mbl = 6;
- n32 = (uint32_t) num;
- rlen = sizeof (res);
- u32_to_u8 (&n32,
- 1,
- res,
- &rlen);
- if ( (1 == rlen) &&
- (res[0] <= 0x1F) )
- {
- lowdump (&buf,
- res[0]);
- }
- else
- {
- GNUNET_buffer_write (&buf,
- (const char *) res,
- rlen);
- }
- }
- break;
- default:
- mbl = 2;
- GNUNET_buffer_write (&buf,
- pos,
- mbl);
- break;
- }
- }
- else
- {
- GNUNET_buffer_write (&buf,
- pos,
- mbl);
- }
- left -= mbl;
- pos += mbl;
- }
-
- /* 0-terminate buffer */
- GNUNET_buffer_write (&buf,
- "",
- 1);
- GNUNET_free (in);
- *inp = GNUNET_buffer_reap (&buf,
- &olen);
- return olen;
-}
-
-
-/**
* Dump the @a json to a string and hash it.
*
* @param json value to hash
@@ -262,7 +98,7 @@ dump_and_hash (const json_t *json,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- len = rfc8785encode (&wire_enc);
+ len = TALER_rfc8785encode (&wire_enc);
if (NULL == salt)
{
GNUNET_CRYPTO_hash (wire_enc,
@@ -819,6 +655,7 @@ parse_path (json_t *obj,
json_t *next_obj = NULL;
char *next_dot;
+ GNUNET_assert (NULL != id); /* make stupid compiler happy */
if (NULL == next_id)
{
cb (cb_cls,
@@ -1031,7 +868,7 @@ TALER_JSON_canonicalize (const json_t *input)
GNUNET_break (0);
return NULL;
}
- rfc8785encode (&wire_enc);
+ TALER_rfc8785encode (&wire_enc);
return wire_enc;
}
diff --git a/src/json/json_wire.c b/src/json/json_wire.c
index 544b56453..9d22d28ea 100644
--- a/src/json/json_wire.c
+++ b/src/json/json_wire.c
@@ -70,80 +70,6 @@ TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s,
}
-enum GNUNET_GenericReturnValue
-TALER_JSON_exchange_wire_signature_check (
- const json_t *wire_s,
- const struct TALER_MasterPublicKeyP *master_pub)
-{
- const char *payto_uri;
- struct TALER_MasterSignatureP master_sig;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("payto_uri",
- &payto_uri),
- GNUNET_JSON_spec_fixed_auto ("master_sig",
- &master_sig),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (wire_s,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- {
- char *err;
-
- err = TALER_payto_validate (payto_uri);
- if (NULL != err)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "URI `%s' ill-formed: %s\n",
- payto_uri,
- err);
- GNUNET_free (err);
- return GNUNET_SYSERR;
- }
- }
-
- return TALER_exchange_wire_signature_check (payto_uri,
- master_pub,
- &master_sig);
-}
-
-
-json_t *
-TALER_JSON_exchange_wire_signature_make (
- const char *payto_uri,
- const struct TALER_MasterPrivateKeyP *master_priv)
-{
- struct TALER_MasterSignatureP master_sig;
- char *err;
-
- if (NULL !=
- (err = TALER_payto_validate (payto_uri)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid payto URI `%s': %s\n",
- payto_uri,
- err);
- GNUNET_free (err);
- return NULL;
- }
- TALER_exchange_wire_signature_make (payto_uri,
- master_priv,
- &master_sig);
- return GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("payto_uri",
- payto_uri),
- GNUNET_JSON_pack_data_auto ("master_sig",
- &master_sig));
-}
-
-
char *
TALER_JSON_wire_to_payto (const json_t *wire_s)
{
diff --git a/src/json/test_json_wire.c b/src/json/test_json_wire.c
deleted file mode 100644
index b417b25fe..000000000
--- a/src/json/test_json_wire.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015, 2016 Taler Systems SA
-
- 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, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file json/test_json_wire.c
- * @brief Tests for Taler-specific crypto logic
- * @author Christian Grothoff <christian@grothoff.org>
- */
-#include "platform.h"
-#include "taler_util.h"
-#include "taler_json_lib.h"
-
-
-int
-main (int argc,
- const char *const argv[])
-{
- struct TALER_MasterPublicKeyP master_pub;
- struct TALER_MasterPrivateKeyP master_priv;
- json_t *wire_xtalerbank;
- json_t *wire_iban;
- const char *payto_xtalerbank = "payto://x-taler-bank/42";
- const char *payto_iban =
- "payto://iban/BIC-TO-BE-SKIPPED/DE89370400440532013000?receiver-name=Test";
- char *p_xtalerbank;
- char *p_iban;
-
- (void) argc;
- (void) argv;
- GNUNET_log_setup ("test-json-wire",
- "WARNING",
- NULL);
- GNUNET_CRYPTO_eddsa_key_create (&master_priv.eddsa_priv);
- GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv,
- &master_pub.eddsa_pub);
- wire_xtalerbank = TALER_JSON_exchange_wire_signature_make (payto_xtalerbank,
- &master_priv);
- wire_iban = TALER_JSON_exchange_wire_signature_make (payto_iban,
- &master_priv);
- if (NULL == wire_iban)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not parse payto/IBAN (%s) into 'wire object'\n",
- payto_iban);
- return 1;
- }
- p_xtalerbank = TALER_JSON_wire_to_payto (wire_xtalerbank);
- p_iban = TALER_JSON_wire_to_payto (wire_iban);
- GNUNET_assert (0 == strcmp (p_xtalerbank, payto_xtalerbank));
- GNUNET_assert (0 == strcmp (p_iban, payto_iban));
- GNUNET_free (p_xtalerbank);
- GNUNET_free (p_iban);
-
- GNUNET_assert (GNUNET_OK ==
- TALER_JSON_exchange_wire_signature_check (wire_xtalerbank,
- &master_pub));
- GNUNET_assert (GNUNET_OK ==
- TALER_JSON_exchange_wire_signature_check (wire_iban,
- &master_pub));
- json_decref (wire_xtalerbank);
- json_decref (wire_iban);
-
- return 0;
-}
-
-
-/* end of test_json_wire.c */
diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am
index 858331f39..20430a4ea 100644
--- a/src/kyclogic/Makefile.am
+++ b/src/kyclogic/Makefile.am
@@ -16,7 +16,11 @@ pkgcfg_DATA = \
EXTRA_DIST = \
$(pkgcfg_DATA) \
- sample.conf
+ sample.conf \
+ persona-sample-reply.json
+
+bin_SCRIPTS = \
+ taler-exchange-kyc-persona-converter.sh
lib_LTLIBRARIES = \
libtalerkyclogic.la
diff --git a/src/kyclogic/kyclogic-persona.conf b/src/kyclogic/kyclogic-persona.conf
index 7f02bf498..2d52a9ee0 100644
--- a/src/kyclogic/kyclogic-persona.conf
+++ b/src/kyclogic/kyclogic-persona.conf
@@ -29,6 +29,10 @@ KYC_PERSONA_SUBDOMAIN = taler
# Authentication token to use.
KYC_PERSONA_AUTH_TOKEN = persona_sandbox_42
+# Program that converts Persona KYC data into the
+# GNU Taler format.
+KYC_PERSONA_CONVERTER_HELPER = taler-exchange-kyc-persona-converter.sh
+
# Form to use.
KYC_PERSONA_TEMPLATE_ID = itempl_Uj6Xxxxx
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
index 0ef1295ed..65f3f3ba3 100644
--- a/src/kyclogic/kyclogic_api.c
+++ b/src/kyclogic/kyclogic_api.c
@@ -780,10 +780,11 @@ TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
TALER_KYCLOGIC_kyc_done ();
return GNUNET_SYSERR;
}
- qsort (kyc_triggers,
- num_kyc_triggers,
- sizeof (struct TALER_KYCLOGIC_KycTrigger *),
- &sort_by_timeframe);
+ if (0 != num_kyc_triggers)
+ qsort (kyc_triggers,
+ num_kyc_triggers,
+ sizeof (struct TALER_KYCLOGIC_KycTrigger *),
+ &sort_by_timeframe);
return GNUNET_OK;
}
diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c
index 228525e28..c72b04b7c 100644
--- a/src/kyclogic/plugin_kyclogic_oauth2.c
+++ b/src/kyclogic/plugin_kyclogic_oauth2.c
@@ -490,8 +490,6 @@ initiate_task (void *cls)
struct PluginState *ps = pd->ps;
char *hps;
char *url;
- char *redirect_uri;
- char *redirect_uri_encoded;
char legi_s[42];
ih->task = NULL;
@@ -501,19 +499,27 @@ initiate_task (void *cls)
(unsigned long long) ih->legitimization_uuid);
hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,
sizeof (ih->h_payto));
- GNUNET_asprintf (&redirect_uri,
- "%skyc-proof/%s?state=%s",
- ps->exchange_base_url,
- pd->section,
- hps);
- redirect_uri_encoded = TALER_urlencode (redirect_uri);
- GNUNET_free (redirect_uri);
- GNUNET_asprintf (&url,
- "%s?response_type=code&client_id=%s&redirect_uri=%s",
- pd->login_url,
- pd->client_id,
- redirect_uri_encoded);
- GNUNET_free (redirect_uri_encoded);
+ {
+ char *redirect_uri_encoded;
+
+ {
+ char *redirect_uri;
+
+ GNUNET_asprintf (&redirect_uri,
+ "%skyc-proof/%s?state=%s",
+ ps->exchange_base_url,
+ pd->section,
+ hps);
+ redirect_uri_encoded = TALER_urlencode (redirect_uri);
+ GNUNET_free (redirect_uri);
+ }
+ GNUNET_asprintf (&url,
+ "%s?response_type=code&client_id=%s&redirect_uri=%s",
+ pd->login_url,
+ pd->client_id,
+ redirect_uri_encoded);
+ GNUNET_free (redirect_uri_encoded);
+ }
/* FIXME-API: why do we *redirect* the client here,
instead of making the HTTP request *ourselves*
and forwarding the response? This prevents us
@@ -583,6 +589,37 @@ oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
/**
+ * Cancel KYC proof.
+ *
+ * @param[in] ph handle of operation to cancel
+ */
+static void
+oauth2_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
+{
+ if (NULL != ph->task)
+ {
+ GNUNET_SCHEDULER_cancel (ph->task);
+ ph->task = NULL;
+ }
+ if (NULL != ph->job)
+ {
+ GNUNET_CURL_job_cancel (ph->job);
+ ph->job = NULL;
+ }
+ if (NULL != ph->response)
+ {
+ MHD_destroy_response (ph->response);
+ ph->response = NULL;
+ }
+ GNUNET_free (ph->provider_user_id);
+ if (NULL != ph->attributes)
+ json_decref (ph->attributes);
+ GNUNET_free (ph->post_body);
+ GNUNET_free (ph);
+}
+
+
+/**
* Function called to asynchronously return the final
* result to the callback.
*
@@ -602,10 +639,8 @@ return_proof_response (void *cls)
ph->attributes,
ph->http_status,
ph->response);
- GNUNET_free (ph->provider_user_id);
- if (NULL != ph->attributes)
- json_decref (ph->attributes);
- GNUNET_free (ph);
+ ph->response = NULL; /*Ownership passed to 'ph->cb'!*/
+ oauth2_proof_cancel (ph);
}
@@ -1101,7 +1136,6 @@ oauth2_proof (void *cls,
1));
{
char *client_id;
- char *redirect_uri;
char *client_secret;
char *authorization_code;
char *redirect_uri_encoded;
@@ -1109,13 +1143,17 @@ oauth2_proof (void *cls,
hps = GNUNET_STRINGS_data_to_string_alloc (&ph->h_payto,
sizeof (ph->h_payto));
- GNUNET_asprintf (&redirect_uri,
- "%skyc-proof/%s?state=%s",
- ps->exchange_base_url,
- pd->section,
- hps);
- redirect_uri_encoded = TALER_urlencode (redirect_uri);
- GNUNET_free (redirect_uri);
+ {
+ char *redirect_uri;
+
+ GNUNET_asprintf (&redirect_uri,
+ "%skyc-proof/%s?state=%s",
+ ps->exchange_base_url,
+ pd->section,
+ hps);
+ redirect_uri_encoded = TALER_urlencode (redirect_uri);
+ GNUNET_free (redirect_uri);
+ }
GNUNET_assert (NULL != redirect_uri_encoded);
client_id = curl_easy_escape (ph->eh,
pd->client_id,
@@ -1165,34 +1203,6 @@ oauth2_proof (void *cls,
/**
- * Cancel KYC proof.
- *
- * @param[in] ph handle of operation to cancel
- */
-static void
-oauth2_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
-{
- if (NULL != ph->task)
- {
- GNUNET_SCHEDULER_cancel (ph->task);
- ph->task = NULL;
- }
- if (NULL != ph->job)
- {
- GNUNET_CURL_job_cancel (ph->job);
- ph->job = NULL;
- }
- if (NULL != ph->response)
- {
- MHD_destroy_response (ph->response);
- ph->response = NULL;
- }
- GNUNET_free (ph->post_body);
- GNUNET_free (ph);
-}
-
-
-/**
* Function to asynchronously return the 404 not found
* page for the webhook.
*
diff --git a/src/kyclogic/plugin_kyclogic_persona.c b/src/kyclogic/plugin_kyclogic_persona.c
index 4f01ae40e..e14a50390 100644
--- a/src/kyclogic/plugin_kyclogic_persona.c
+++ b/src/kyclogic/plugin_kyclogic_persona.c
@@ -112,6 +112,12 @@ struct TALER_KYCLOGIC_ProviderDetails
char *subdomain;
/**
+ * Name of the program we use to convert outputs
+ * from Persona into our JSON inputs.
+ */
+ char *conversion_binary;
+
+ /**
* Where to redirect the client upon completion.
*/
char *post_kyc_redirect_url;
@@ -231,6 +237,12 @@ struct TALER_KYCLOGIC_ProofHandle
char *url;
/**
+ * Handle to an external process that converts the
+ * Persona response to our internal format.
+ */
+ struct TALER_JSON_ExternalConversion *ec;
+
+ /**
* Hash of the payto:// URI we are checking the KYC for.
*/
struct TALER_PaytoHashP h_payto;
@@ -247,6 +259,11 @@ struct TALER_KYCLOGIC_ProofHandle
char *provider_user_id;
/**
+ * Account ID from the service.
+ */
+ char *account_id;
+
+ /**
* Inquiry ID at the provider.
*/
char *inquiry_id;
@@ -295,6 +312,11 @@ struct TALER_KYCLOGIC_WebhookHandle
char *inquiry_id;
/**
+ * Account ID from the service.
+ */
+ char *account_id;
+
+ /**
* URL of the cURL request.
*/
char *url;
@@ -316,6 +338,12 @@ struct TALER_KYCLOGIC_WebhookHandle
const char *template_id;
/**
+ * Handle to an external process that converts the
+ * Persona response to our internal format.
+ */
+ struct TALER_JSON_ExternalConversion *ec;
+
+ /**
* Our account ID.
*/
struct TALER_PaytoHashP h_payto;
@@ -344,6 +372,7 @@ persona_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
GNUNET_free (pd->auth_token);
GNUNET_free (pd->template_id);
GNUNET_free (pd->subdomain);
+ GNUNET_free (pd->conversion_binary);
GNUNET_free (pd->salt);
GNUNET_free (pd->section);
GNUNET_free (pd->post_kyc_redirect_url);
@@ -421,6 +450,18 @@ persona_load_configuration (void *cls,
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
provider_section_name,
+ "KYC_PERSONA_CONVERTER_HELPER",
+ &pd->conversion_binary))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ provider_section_name,
+ "KYC_PERSONA_CONVERTER_HELPER");
+ persona_unload_configuration (pd);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (ps->cfg,
+ provider_section_name,
"KYC_PERSONA_POST_URL",
&pd->post_kyc_redirect_url))
{
@@ -838,8 +879,14 @@ persona_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
GNUNET_CURL_job_cancel (ph->job);
ph->job = NULL;
}
+ if (NULL != ph->ec)
+ {
+ TALER_JSON_external_conversion_stop (ph->ec);
+ ph->ec = NULL;
+ }
GNUNET_free (ph->url);
GNUNET_free (ph->provider_user_id);
+ GNUNET_free (ph->account_id);
GNUNET_free (ph->inquiry_id);
GNUNET_free (ph);
}
@@ -923,161 +970,6 @@ proof_reply_error (struct TALER_KYCLOGIC_ProofHandle *ph,
/**
- * Convert KYC attribute data from Persona response.
- *
- * @param attr json array with Persona attribute data
- * @return KYC attribute data
- */
-static json_t *
-convert_attributes (const json_t *attr)
-{
- const char *country_code = NULL;
- const char *name_first = NULL;
- const char *name_middle = NULL;
- const char *name_last = NULL;
- const char *address_street_1 = NULL;
- const char *address_street_2 = NULL;
- const char *address_city = NULL;
- const char *address_postal_code = NULL;
- const char *birthdate = NULL;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("country-code",
- &country_code),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("name-first",
- &name_first),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("name-middle",
- &name_middle),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("name-last",
- &name_last),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("address-street-1",
- &address_street_1),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("address-street-2",
- &address_street_2),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("address-city",
- &address_city),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("address-postal-code",
- &address_postal_code),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("birthdate",
- &birthdate),
- NULL),
- GNUNET_JSON_spec_end ()
- };
- json_t *ret;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (attr,
- spec,
- NULL, NULL))
- {
- GNUNET_break (0);
- json_dumpf (attr,
- stderr,
- JSON_INDENT (2));
- return NULL;
- }
- {
- char *name = NULL;
- char *street = NULL;
- char *city = NULL;
-
- if ( (NULL != name_last) ||
- (NULL != name_first) ||
- (NULL != name_middle) )
- {
- GNUNET_asprintf (&name,
- "%s, %s %s",
- (NULL != name_last)
- ? name_last
- : "",
- (NULL != name_first)
- ? name_first
- : "",
- (NULL != name_middle)
- ? name_middle
- : "");
- }
- if ( (NULL != address_city) ||
- (NULL != address_postal_code) )
- {
- GNUNET_asprintf (&city,
- "%s%s%s %s",
- (NULL != country_code)
- ? country_code
- : "",
- (NULL != country_code)
- ? "-"
- : "",
- (NULL != address_postal_code)
- ? address_postal_code
- : "",
- (NULL != address_city)
- ? address_city
- : "");
- }
- if ( (NULL != address_street_1) ||
- (NULL != address_street_2) )
- {
- GNUNET_asprintf (&street,
- "%s%s%s",
- (NULL != address_street_1)
- ? address_street_1
- : "",
- ( (NULL != address_street_1) &&
- (NULL != address_street_2) )
- ? "\n"
- : "",
- (NULL != address_street_2)
- ? address_street_2
- : "");
- }
- ret = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_BIRTHDATE,
- birthdate)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_FULL_NAME,
- name)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_ADDRESS_STREET,
- street)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_ADDRESS_CITY,
- city)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_RESIDENCES,
- country_code))
- );
- GNUNET_free (street);
- GNUNET_free (city);
- GNUNET_free (name);
- }
- return ret;
-}
-
-
-/**
* Return a response for the @a ph request indicating a
* protocol violation by the Persona server.
*
@@ -1116,6 +1008,86 @@ return_invalid_response (struct TALER_KYCLOGIC_ProofHandle *ph,
/**
+ * Start the external conversion helper.
+ *
+ * @param pd configuration details
+ * @param attr attributes to give to the helper
+ * @param cb function to call with the result
+ * @param cb_cls closure for @a cb
+ * @return handle for the helper
+ */
+static struct TALER_JSON_ExternalConversion *
+start_conversion (const struct TALER_KYCLOGIC_ProviderDetails *pd,
+ const json_t *attr,
+ TALER_JSON_JsonCallback cb,
+ void *cb_cls)
+{
+ return TALER_JSON_external_conversion_start (
+ attr,
+ cb,
+ cb_cls,
+ pd->conversion_binary,
+ pd->conversion_binary,
+ "-a",
+ pd->auth_token,
+ NULL
+ );
+}
+
+
+/**
+ * Type of a callback that receives a JSON @a result.
+ *
+ * @param cls closure with a `struct TALER_KYCLOGIC_ProofHandle *`
+ * @param status_type how did the process die
+ * @param code termination status code from the process
+ * @param attr result some JSON result, NULL if we failed to get an JSON output
+ */
+static void
+proof_post_conversion_cb (void *cls,
+ enum GNUNET_OS_ProcessStatusType status_type,
+ unsigned long code,
+ const json_t *attr)
+{
+ struct TALER_KYCLOGIC_ProofHandle *ph = cls;
+ struct MHD_Response *resp;
+ struct GNUNET_TIME_Absolute expiration;
+
+ ph->ec = NULL;
+ if ( (NULL == attr) ||
+ (0 != code) )
+ {
+ GNUNET_break_op (0);
+ return_invalid_response (ph,
+ MHD_HTTP_OK,
+ ph->inquiry_id,
+ "converter",
+ NULL);
+ persona_proof_cancel (ph);
+ return;
+ }
+ expiration = GNUNET_TIME_relative_to_absolute (ph->pd->validity);
+ resp = MHD_create_response_from_buffer (0,
+ "",
+ MHD_RESPMEM_PERSISTENT);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_LOCATION,
+ ph->pd->post_kyc_redirect_url));
+ TALER_MHD_add_global_headers (resp);
+ ph->cb (ph->cb_cls,
+ TALER_KYCLOGIC_STATUS_SUCCESS,
+ ph->account_id,
+ ph->inquiry_id,
+ expiration,
+ attr,
+ MHD_HTTP_SEE_OTHER,
+ resp);
+ persona_proof_cancel (ph);
+}
+
+
+/**
* Function called when we're done processing the
* HTTP "/api/v1/inquiries/{inquiry-id}" request.
*
@@ -1283,46 +1255,15 @@ handle_proof_finished (void *cls,
data);
break;
}
-
- {
- struct MHD_Response *resp;
- struct GNUNET_TIME_Absolute expiration;
- json_t *attr;
-
- attr = convert_attributes (attributes);
- if (NULL == attr)
- {
- GNUNET_break_op (0);
- return_invalid_response (ph,
- response_code,
- inquiry_id,
- "data-relationships-account-data-id",
- data);
- break;
- }
- expiration = GNUNET_TIME_relative_to_absolute (ph->pd->validity);
- resp = MHD_create_response_from_buffer (0,
- "",
- MHD_RESPMEM_PERSISTENT);
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (resp,
- MHD_HTTP_HEADER_LOCATION,
- ph->pd->post_kyc_redirect_url));
- TALER_MHD_add_global_headers (resp);
- ph->cb (ph->cb_cls,
- TALER_KYCLOGIC_STATUS_SUCCESS,
- account_id,
- inquiry_id,
- expiration,
- attr,
- MHD_HTTP_SEE_OTHER,
- resp);
- json_decref (attr);
- }
+ ph->account_id = GNUNET_strdup (account_id);
+ ph->ec = start_conversion (ph->pd,
+ j,
+ &proof_post_conversion_cb,
+ ph);
GNUNET_JSON_parse_free (ispec);
}
GNUNET_JSON_parse_free (spec);
- break;
+ return; /* continued in proof_post_conversion_cb */
}
case MHD_HTTP_BAD_REQUEST:
case MHD_HTTP_NOT_FOUND:
@@ -1580,6 +1521,12 @@ persona_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
GNUNET_CURL_job_cancel (wh->job);
wh->job = NULL;
}
+ if (NULL != wh->ec)
+ {
+ TALER_JSON_external_conversion_stop (wh->ec);
+ wh->ec = NULL;
+ }
+ GNUNET_free (wh->account_id);
GNUNET_free (wh->inquiry_id);
GNUNET_free (wh->url);
GNUNET_free (wh);
@@ -1651,6 +1598,32 @@ webhook_reply_error (struct TALER_KYCLOGIC_WebhookHandle *wh,
/**
+ * Type of a callback that receives a JSON @a result.
+ *
+ * @param cls closure with a `struct TALER_KYCLOGIC_WebhookHandle *`
+ * @param status_type how did the process die
+ * @param code termination status code from the process
+ * @param attr some JSON result, NULL if we failed to get an JSON output
+ */
+static void
+webhook_post_conversion_cb (void *cls,
+ enum GNUNET_OS_ProcessStatusType status_type,
+ unsigned long code,
+ const json_t *attr)
+{
+ struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
+
+ wh->ec = NULL;
+ webhook_generic_reply (wh,
+ TALER_KYCLOGIC_STATUS_SUCCESS,
+ wh->account_id,
+ wh->inquiry_id,
+ attr,
+ MHD_HTTP_OK);
+}
+
+
+/**
* Function called when we're done processing the
* HTTP "/api/v1/inquiries/{inquiry_id}" request.
*
@@ -1723,7 +1696,6 @@ handle_webhook_finished (void *cls,
NULL),
GNUNET_JSON_spec_end ()
};
- json_t *attr;
if (GNUNET_OK !=
GNUNET_JSON_parse (attributes,
@@ -1807,19 +1779,15 @@ handle_webhook_finished (void *cls,
MHD_HTTP_BAD_GATEWAY);
break;
}
-
- attr = convert_attributes (attributes);
- webhook_generic_reply (wh,
- TALER_KYCLOGIC_STATUS_SUCCESS,
- account_id,
- inquiry_id,
- attr,
- MHD_HTTP_OK);
- json_decref (attr);
+ wh->account_id = GNUNET_strdup (account_id);
+ wh->ec = start_conversion (wh->pd,
+ j,
+ &webhook_post_conversion_cb,
+ wh);
GNUNET_JSON_parse_free (ispec);
}
GNUNET_JSON_parse_free (spec);
- break;
+ return; /* continued in webhook_post_conversion_cb */
}
case MHD_HTTP_BAD_REQUEST:
case MHD_HTTP_NOT_FOUND:
diff --git a/src/kyclogic/taler-exchange-kyc-persona-converter.sh b/src/kyclogic/taler-exchange-kyc-persona-converter.sh
new file mode 100755
index 000000000..a5d4d03ac
--- /dev/null
+++ b/src/kyclogic/taler-exchange-kyc-persona-converter.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+# This file is in the public domain.
+#
+# This code converts (some of) the JSON output from Persona into the GNU Taler
+# specific KYC attribute data (again in JSON format). We may need to download
+# and inline file data in the process, for authorization pass "-a" with the
+# respective bearer token.
+#
+
+# Die if anything goes wrong.
+set -eu
+
+# Parse command-line options
+while getopts ':a:' OPTION; do
+ case "$OPTION" in
+ a)
+ TOKEN="$OPTARG"
+ ;;
+ ?)
+ echo "Unrecognized command line option"
+ exit 1
+ ;;
+ esac
+done
+
+
+# First, extract everything from stdin.
+J=$(jq '{"first":.data.attributes."name-first","middle":.data.attributes."name-middle","last":.data.attributes."name-last","cc":.data.attributes.fields."address-country-code".value,"birthdate":.data.attributes.birthdate,"city":.data.attributes."address-city","postcode":.data.attributes."address-postal-code","street-1":.data.attributes."address-street-1","street-2":.data.attributes."address-street-2","address-subdivision":.data.attributes."address-subdivision","identification-number":.data.attributes."identification-number","photo":.included[]|select(.type=="verification/government-id")|.attributes|select(.status=="passed")|."front-photo-url"}')
+
+
+# Next, combine some fields into larger values.
+FULLNAME=$(echo "$J" | jq -r '[.first,.middle,.last]|join(" ")')
+STREET=$(echo $J | jq -r '[."street-1",."street-2"]|join(" ")')
+CITY=$(echo $J | jq -r '[.postcode,.city,."address-subdivision,.cc"]|join(" ")')
+
+# Download and base32-encode the photo
+PHOTO_URL=$(echo "$J" | jq -r '.photo')
+PHOTO_FILE=$(mktemp -t tmp.XXXXXXXXXX)
+if [ -z "${TOKEN:-}" ]
+then
+ wget -q --output-document=- "$PHOTO_URL" | gnunet-base32 > ${PHOTO_FILE}
+else
+ wget -q --output-document=- --header "Authorization: Bearer $TOKEN" "$PHOTO_URL" | gnunet-base32 > ${PHOTO_FILE}
+fi
+
+# Combine into final result.
+echo "$J" | jq \
+ --arg full_name "${FULLNAME}" \
+ --arg street "${STREET}" \
+ --arg city "${CITY}" \
+ --rawfile photo "${PHOTO_FILE}" \
+ '{$full_name,$street,$city,"birthdate":.birthdate,"residences":.cc,"identification_number":."identification-number",$photo}'
+
+exit 0
diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c
index 652d498c6..c2efafd72 100644
--- a/src/kyclogic/taler-exchange-kyc-tester.c
+++ b/src/kyclogic/taler-exchange-kyc-tester.c
@@ -990,9 +990,9 @@ proceed_with_handler (struct TEKT_RequestContext *rc,
/* Parse command-line arguments */
/* make a copy of 'url' because 'strtok_r()' will modify */
- memcpy (d,
- url,
- ulen);
+ GNUNET_memcpy (d,
+ url,
+ ulen);
i = 0;
args[i++] = strtok_r (d, "/", &sp);
while ( (NULL != args[i - 1]) &&
diff --git a/src/lib/exchange_api_batch_deposit.c b/src/lib/exchange_api_batch_deposit.c
index 4665908d2..c583d5add 100644
--- a/src/lib/exchange_api_batch_deposit.c
+++ b/src/lib/exchange_api_batch_deposit.c
@@ -247,7 +247,7 @@ handle_deposit_finished (void *cls,
&dh->exchange_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("transaction_base_url",
- &dr.details.success.transaction_base_url),
+ &dr.details.ok.transaction_base_url),
NULL),
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
&dh->exchange_timestamp),
@@ -332,7 +332,7 @@ handle_deposit_finished (void *cls,
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
- GNUNET_JSON_parse_free (spec);
+ GNUNET_JSON_parse_free (spec);
break;
}
}
@@ -341,10 +341,10 @@ handle_deposit_finished (void *cls,
dh);
GNUNET_JSON_parse_free (spec);
}
- dr.details.success.exchange_sigs = dh->exchange_sigs;
- dr.details.success.exchange_pub = &dh->exchange_pub;
- dr.details.success.deposit_timestamp = dh->exchange_timestamp;
- dr.details.success.num_signatures = dh->num_cdds;
+ dr.details.ok.exchange_sigs = dh->exchange_sigs;
+ dr.details.ok.exchange_pub = &dh->exchange_pub;
+ dr.details.ok.deposit_timestamp = dh->exchange_timestamp;
+ dr.details.ok.num_signatures = dh->num_cdds;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
diff --git a/src/lib/exchange_api_batch_withdraw.c b/src/lib/exchange_api_batch_withdraw.c
index 9bb158f87..4817ae403 100644
--- a/src/lib/exchange_api_batch_withdraw.c
+++ b/src/lib/exchange_api_batch_withdraw.c
@@ -144,37 +144,34 @@ struct TALER_EXCHANGE_BatchWithdrawHandle
* HTTP /reserves/$RESERVE_PUB/batch-withdraw request.
*
* @param cls the `struct TALER_EXCHANGE_BatchWithdrawHandle`
- * @param hr HTTP response data
- * @param blind_sigs array of blind signatures over the coins, NULL on error
- * @param blind_sigs_length length of the @a blind_sigs array
+ * @param bw2r response data
*/
static void
handle_reserve_batch_withdraw_finished (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_BlindedDenominationSignature *blind_sigs,
- unsigned int blind_sigs_length)
+ const struct TALER_EXCHANGE_BatchWithdraw2Response *bw2r)
{
struct TALER_EXCHANGE_BatchWithdrawHandle *wh = cls;
struct TALER_EXCHANGE_BatchWithdrawResponse wr = {
- .hr = *hr
+ .hr = bw2r->hr
};
- struct TALER_EXCHANGE_PrivateCoinDetails coins[wh->num_coins];
+ struct TALER_EXCHANGE_PrivateCoinDetails coins[GNUNET_NZL (wh->num_coins)];
wh->wh2 = NULL;
memset (coins,
0,
sizeof (coins));
- if (blind_sigs_length != wh->num_coins)
- {
- GNUNET_break_op (0);
- wr.hr.http_status = 0;
- wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- }
- switch (hr->http_status)
+ switch (bw2r->hr.http_status)
{
case MHD_HTTP_OK:
{
+ if (bw2r->details.ok.blind_sigs_length != wh->num_coins)
+ {
+ GNUNET_break_op (0);
+ wr.hr.http_status = 0;
+ wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ break;
+ }
for (unsigned int i = 0; i<wh->num_coins; i++)
{
struct CoinData *cd = &wh->coins[i];
@@ -183,7 +180,7 @@ handle_reserve_batch_withdraw_finished (
if (GNUNET_OK !=
TALER_planchet_to_coin (&cd->pk.key,
- &blind_sigs[i],
+ &bw2r->details.ok.blind_sigs[i],
&cd->bks,
&cd->priv,
cd->ach,
@@ -200,8 +197,8 @@ handle_reserve_batch_withdraw_finished (
coin->sig = fc.sig;
coin->exchange_vals = cd->alg_values;
}
- wr.details.success.coins = coins;
- wr.details.success.num_coins = wh->num_coins;
+ wr.details.ok.coins = coins;
+ wr.details.ok.num_coins = wh->num_coins;
break;
}
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
@@ -217,7 +214,7 @@ handle_reserve_batch_withdraw_finished (
};
if (GNUNET_OK !=
- GNUNET_JSON_parse (hr->reply,
+ GNUNET_JSON_parse (bw2r->hr.reply,
spec,
NULL, NULL))
{
@@ -289,7 +286,7 @@ withdraw_cs_stage_two_callback (
switch (csrr->hr.http_status)
{
case MHD_HTTP_OK:
- cd->alg_values = csrr->details.success.alg_values;
+ cd->alg_values = csrr->details.ok.alg_values;
TALER_planchet_setup_coin_priv (&cd->ps,
&cd->alg_values,
&cd->priv);
diff --git a/src/lib/exchange_api_batch_withdraw2.c b/src/lib/exchange_api_batch_withdraw2.c
index e14ed92fd..04c2c0100 100644
--- a/src/lib/exchange_api_batch_withdraw2.c
+++ b/src/lib/exchange_api_batch_withdraw2.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2022 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
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
@@ -108,9 +108,9 @@ reserve_batch_withdraw_ok (struct TALER_EXCHANGE_BatchWithdraw2Handle *wh,
"ev_sigs");
const json_t *j;
unsigned int index;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = json,
- .http_status = MHD_HTTP_OK
+ struct TALER_EXCHANGE_BatchWithdraw2Response bwr = {
+ .hr.reply = json,
+ .hr.http_status = MHD_HTTP_OK
};
if ( (NULL == ja) ||
@@ -141,10 +141,10 @@ reserve_batch_withdraw_ok (struct TALER_EXCHANGE_BatchWithdraw2Handle *wh,
}
/* signature is valid, return it to the application */
+ bwr.details.ok.blind_sigs = blind_sigs;
+ bwr.details.ok.blind_sigs_length = wh->num_coins;
wh->cb (wh->cb_cls,
- &hr,
- blind_sigs,
- wh->num_coins);
+ &bwr);
/* make sure callback isn't called again after return */
wh->cb = NULL;
for (unsigned int i = 0; i<wh->num_coins; i++)
@@ -264,16 +264,16 @@ handle_reserve_batch_withdraw_finished (void *cls,
{
struct TALER_EXCHANGE_BatchWithdraw2Handle *wh = cls;
const json_t *j = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_BatchWithdraw2Response bwr = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
};
wh->job = NULL;
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ bwr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
@@ -281,8 +281,8 @@ handle_reserve_batch_withdraw_finished (void *cls,
j))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ bwr.hr.http_status = 0;
+ bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
GNUNET_assert (NULL == wh->cb);
@@ -304,8 +304,8 @@ handle_reserve_batch_withdraw_finished (void *cls,
NULL, NULL))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ bwr.hr.http_status = 0;
+ bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
}
@@ -313,24 +313,24 @@ handle_reserve_batch_withdraw_finished (void *cls,
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ bwr.hr.ec = TALER_JSON_get_error_code (j);
+ bwr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_FORBIDDEN:
GNUNET_break_op (0);
/* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ bwr.hr.ec = TALER_JSON_get_error_code (j);
+ bwr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, the exchange basically just says
that it doesn't know this reserve. Can happen if we
query before the wire transfer went through.
We should simply pass the JSON reply to the application. */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ bwr.hr.ec = TALER_JSON_get_error_code (j);
+ bwr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
/* The exchange says that the reserve has insufficient funds;
@@ -340,13 +340,13 @@ handle_reserve_batch_withdraw_finished (void *cls,
j))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ bwr.hr.http_status = 0;
+ bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
}
else
{
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ bwr.hr.ec = TALER_JSON_get_error_code (j);
+ bwr.hr.hint = TALER_JSON_get_error_hint (j);
}
break;
case MHD_HTTP_GONE:
@@ -354,32 +354,30 @@ handle_reserve_batch_withdraw_finished (void *cls,
/* Note: one might want to check /keys for revocation
signature here, alas tricky in case our /keys
is outdated => left to clients */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ bwr.hr.ec = TALER_JSON_get_error_code (j);
+ bwr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ bwr.hr.ec = TALER_JSON_get_error_code (j);
+ bwr.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ bwr.hr.ec = TALER_JSON_get_error_code (j);
+ bwr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange batch withdraw\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) bwr.hr.ec);
break;
}
if (NULL != wh->cb)
{
wh->cb (wh->cb_cls,
- &hr,
- NULL,
- 0);
+ &bwr);
wh->cb = NULL;
}
TALER_EXCHANGE_batch_withdraw2_cancel (wh);
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index b895bf9a8..588b12834 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -2194,4 +2194,196 @@ TALER_EXCHANGE_verify_deposit_signature_ (
}
+/**
+ * Parse account restriction in @a jrest into @a rest.
+ *
+ * @param jresta array of account restrictions in JSON
+ * @param[out] resta_len set to length of @a resta
+ * @param[out] resta account restriction array to set
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_restrictions (const json_t *jresta,
+ unsigned int *resta_len,
+ struct TALER_EXCHANGE_AccountRestriction **resta)
+{
+ if (! json_is_array (jresta))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ *resta_len = json_array_size (jresta);
+ if (0 == *resta_len)
+ {
+ /* no restrictions, perfectly OK */
+ *resta = NULL;
+ return GNUNET_OK;
+ }
+ *resta = GNUNET_new_array (*resta_len,
+ struct TALER_EXCHANGE_AccountRestriction);
+ for (unsigned int i = 0; i<*resta_len; i++)
+ {
+ const json_t *jr = json_array_get (jresta,
+ i);
+ struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i];
+ const char *type = json_string_value (json_object_get (jr,
+ "type"));
+
+ if (NULL == type)
+ {
+ GNUNET_break (0);
+ goto fail;
+ }
+ if (0 == strcmp (type,
+ "deny"))
+ {
+ ar->type = TALER_EXCHANGE_AR_DENY;
+ continue;
+ }
+ if (0 == strcmp (type,
+ "regex"))
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string (
+ "payto_regex",
+ &ar->details.regex.posix_egrep),
+ GNUNET_JSON_spec_string (
+ "human_hint",
+ &ar->details.regex.human_hint),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const (
+ "human_hint_i18n",
+ &ar->details.regex.human_hint_i18n),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jr,
+ spec,
+ NULL, NULL))
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ goto fail;
+ }
+ ar->type = TALER_EXCHANGE_AR_REGEX;
+ continue;
+ }
+ /* unsupported type */
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+fail:
+ GNUNET_free (*resta);
+ *resta_len = 0;
+ return GNUNET_SYSERR;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_parse_accounts (const struct TALER_MasterPublicKeyP *master_pub,
+ const json_t *accounts,
+ struct TALER_EXCHANGE_WireAccount was[],
+ unsigned int was_length)
+{
+ memset (was,
+ 0,
+ sizeof (struct TALER_EXCHANGE_WireAccount) * was_length);
+ GNUNET_assert (was_length ==
+ json_array_size (accounts));
+ for (unsigned int i = 0;
+ i<was_length;
+ i++)
+ {
+ struct TALER_EXCHANGE_WireAccount *wa = &was[i];
+ json_t *credit_restrictions;
+ json_t *debit_restrictions;
+ struct GNUNET_JSON_Specification spec_account[] = {
+ GNUNET_JSON_spec_string ("payto_uri",
+ &wa->payto_uri),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("conversion_url",
+ &wa->conversion_url),
+ NULL),
+ GNUNET_JSON_spec_json ("credit_restrictions",
+ &credit_restrictions),
+ GNUNET_JSON_spec_json ("debit_restrictions",
+ &debit_restrictions),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &wa->master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ json_t *account;
+
+ account = json_array_get (accounts,
+ i);
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (account,
+ spec_account,
+ NULL, NULL))
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ char *err;
+
+ err = TALER_payto_validate (wa->payto_uri);
+ if (NULL != err)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (err);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ if ( (NULL != master_pub) &&
+ (GNUNET_OK !=
+ TALER_exchange_wire_signature_check (wa->payto_uri,
+ wa->conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ master_pub,
+ &wa->master_sig)) )
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if ( (GNUNET_OK !=
+ parse_restrictions (credit_restrictions,
+ &wa->credit_restrictions_length,
+ &wa->credit_restrictions)) ||
+ (GNUNET_OK !=
+ parse_restrictions (debit_restrictions,
+ &wa->debit_restrictions_length,
+ &wa->debit_restrictions)) )
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_JSON_parse_free (spec_account);
+ } /* end 'for all accounts */
+ return GNUNET_OK;
+}
+
+
+void
+TALER_EXCHANGE_free_accounts (struct TALER_EXCHANGE_WireAccount *was,
+ unsigned int was_len)
+{
+ for (unsigned int i = 0; i<was_len; i++)
+ {
+ struct TALER_EXCHANGE_WireAccount *wa = &was[i];
+
+ GNUNET_free (wa->credit_restrictions);
+ GNUNET_free (wa->debit_restrictions);
+ }
+}
+
+
/* end of exchange_api_common.c */
diff --git a/src/lib/exchange_api_contracts_get.c b/src/lib/exchange_api_contracts_get.c
index 263a0cbe9..8fd4ba1e7 100644
--- a/src/lib/exchange_api_contracts_get.c
+++ b/src/lib/exchange_api_contracts_get.c
@@ -109,7 +109,7 @@ handle_contract_get_finished (void *cls,
struct TALER_PurseContractSignatureP econtract_sig;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("purse_pub",
- &dr.details.success.purse_pub),
+ &dr.details.ok.purse_pub),
GNUNET_JSON_spec_fixed_auto ("econtract_sig",
&econtract_sig),
GNUNET_JSON_spec_varsize ("econtract",
@@ -133,7 +133,7 @@ handle_contract_get_finished (void *cls,
econtract,
econtract_size,
&cgh->cpub,
- &dr.details.success.purse_pub,
+ &dr.details.ok.purse_pub,
&econtract_sig))
{
GNUNET_break (0);
@@ -142,8 +142,8 @@ handle_contract_get_finished (void *cls,
GNUNET_JSON_parse_free (spec);
break;
}
- dr.details.success.econtract = econtract;
- dr.details.success.econtract_size = econtract_size;
+ dr.details.ok.econtract = econtract;
+ dr.details.ok.econtract_size = econtract_size;
cgh->cb (cgh->cb_cls,
&dr);
GNUNET_JSON_parse_free (spec);
diff --git a/src/lib/exchange_api_csr_melt.c b/src/lib/exchange_api_csr_melt.c
index 9de8cd8d9..67b1a9b7e 100644
--- a/src/lib/exchange_api_csr_melt.c
+++ b/src/lib/exchange_api_csr_melt.c
@@ -94,8 +94,8 @@ csr_ok (struct TALER_EXCHANGE_CsRMeltHandle *csrh,
struct TALER_ExchangeWithdrawValues alg_values[GNUNET_NZL (alen)];
struct TALER_EXCHANGE_CsRMeltResponse csrr = {
.hr = *hr,
- .details.success.alg_values_len = alen,
- .details.success.alg_values = alg_values
+ .details.ok.alg_values_len = alen,
+ .details.ok.alg_values = alg_values
};
for (unsigned int i = 0; i<alen; i++)
diff --git a/src/lib/exchange_api_csr_withdraw.c b/src/lib/exchange_api_csr_withdraw.c
index fa806857d..fca3fff8e 100644
--- a/src/lib/exchange_api_csr_withdraw.c
+++ b/src/lib/exchange_api_csr_withdraw.c
@@ -96,7 +96,7 @@ csr_ok (struct TALER_EXCHANGE_CsRWithdrawHandle *csrh,
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_exchange_withdraw_values (
"ewv",
- &csrr.details.success.alg_values),
+ &csrr.details.ok.alg_values),
GNUNET_JSON_spec_end ()
};
diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c
index 8f179b72c..5a0073f3f 100644
--- a/src/lib/exchange_api_deposit.c
+++ b/src/lib/exchange_api_deposit.c
@@ -236,7 +236,7 @@ handle_deposit_finished (void *cls,
&dh->exchange_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("transaction_base_url",
- &dr.details.success.transaction_base_url),
+ &dr.details.ok.transaction_base_url),
NULL),
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
&dh->exchange_timestamp),
@@ -297,9 +297,9 @@ handle_deposit_finished (void *cls,
&auditor_cb,
dh);
}
- dr.details.success.exchange_sig = &dh->exchange_sig;
- dr.details.success.exchange_pub = &dh->exchange_pub;
- dr.details.success.deposit_timestamp = dh->exchange_timestamp;
+ dr.details.ok.exchange_sig = &dh->exchange_sig;
+ dr.details.ok.exchange_pub = &dh->exchange_pub;
+ dr.details.ok.deposit_timestamp = dh->exchange_timestamp;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
diff --git a/src/lib/exchange_api_deposits_get.c b/src/lib/exchange_api_deposits_get.c
index 2e8a5e5e8..9ec25e45a 100644
--- a/src/lib/exchange_api_deposits_get.c
+++ b/src/lib/exchange_api_deposits_get.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
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
@@ -118,15 +118,15 @@ handle_deposit_wtid_finished (void *cls,
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("wtid",
- &dr.details.success.wtid),
+ &dr.details.ok.wtid),
GNUNET_JSON_spec_timestamp ("execution_time",
- &dr.details.success.execution_time),
+ &dr.details.ok.execution_time),
TALER_JSON_spec_amount_any ("coin_contribution",
- &dr.details.success.coin_contribution),
+ &dr.details.ok.coin_contribution),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
- &dr.details.success.exchange_sig),
+ &dr.details.ok.exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
- &dr.details.success.exchange_pub),
+ &dr.details.ok.exchange_pub),
GNUNET_JSON_spec_end ()
};
const struct TALER_EXCHANGE_Keys *key_state;
@@ -145,7 +145,7 @@ handle_deposit_wtid_finished (void *cls,
}
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_state,
- &dr.details.success.exchange_pub))
+ &dr.details.ok.exchange_pub))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
@@ -156,12 +156,12 @@ handle_deposit_wtid_finished (void *cls,
TALER_exchange_online_confirm_wire_verify (
&dwh->h_wire,
&dwh->h_contract_terms,
- &dr.details.success.wtid,
+ &dr.details.ok.wtid,
&dwh->coin_pub,
- dr.details.success.execution_time,
- &dr.details.success.coin_contribution,
- &dr.details.success.exchange_pub,
- &dr.details.success.exchange_sig))
+ dr.details.ok.execution_time,
+ &dr.details.ok.coin_contribution,
+ &dr.details.ok.exchange_pub,
+ &dr.details.ok.exchange_sig))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
@@ -260,6 +260,7 @@ TALER_EXCHANGE_deposits_get (
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct GNUNET_TIME_Relative timeout,
TALER_EXCHANGE_DepositGetCallback cb,
void *cb_cls)
{
@@ -293,6 +294,7 @@ TALER_EXCHANGE_deposits_get (
char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2];
char chash_str[sizeof (struct TALER_PrivateContractHashP) * 2];
char whash_str[sizeof (struct TALER_MerchantWireHashP) * 2];
+ char timeout_str[24];
char *end;
end = GNUNET_STRINGS_data_to_string (h_wire,
@@ -320,15 +322,33 @@ TALER_EXCHANGE_deposits_get (
msig_str,
sizeof (msig_str));
*end = '\0';
+ if (GNUNET_TIME_relative_is_zero (timeout))
+ {
+ timeout_str[0] = '\0';
+ }
+ else
+ {
+ GNUNET_snprintf (
+ timeout_str,
+ sizeof (timeout_str),
+ "%llu",
+ (unsigned long long) (
+ timeout.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us));
+ }
GNUNET_snprintf (arg_str,
sizeof (arg_str),
- "/deposits/%s/%s/%s/%s?merchant_sig=%s",
+ "/deposits/%s/%s/%s/%s?merchant_sig=%s%s%s",
whash_str,
mpub_str,
chash_str,
cpub_str,
- msig_str);
+ msig_str,
+ GNUNET_TIME_relative_is_zero (timeout)
+ ? ""
+ : "&timeout_ms=",
+ timeout_str);
}
dwh = GNUNET_new (struct TALER_EXCHANGE_DepositGetHandle);
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index 8501ed11d..0e8046305 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2022 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
@@ -40,7 +40,7 @@
* Which version of the Taler protocol is implemented
* by this library? Used to determine compatibility.
*/
-#define EXCHANGE_PROTOCOL_CURRENT 14
+#define EXCHANGE_PROTOCOL_CURRENT 15
/**
* How many versions are we backwards compatible with?
@@ -1305,15 +1305,18 @@ keys_completed_cb (void *cls,
{
struct KeysRequest *kr = cls;
struct TALER_EXCHANGE_Handle *exchange = kr->exchange;
- struct TALER_EXCHANGE_Keys kd;
struct TALER_EXCHANGE_Keys kd_old;
- enum TALER_EXCHANGE_VersionCompatibility vc;
const json_t *j = resp_obj;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_Keys kd;
+ struct TALER_EXCHANGE_KeysResponse kresp = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code,
+ .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR,
};
+ memset (&kd,
+ 0,
+ sizeof (kd));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received keys from URL `%s' with status %ld and expiration %s.\n",
kr->url,
@@ -1330,10 +1333,6 @@ keys_completed_cb (void *cls,
GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION));
}
kd_old = exchange->key_data;
- memset (&kd,
- 0,
- sizeof (struct TALER_EXCHANGE_Keys));
- vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
switch (response_code)
{
case 0:
@@ -1358,17 +1357,19 @@ keys_completed_cb (void *cls,
}
/* We keep the denomination keys and auditor signatures from the
previous iteration (/keys cherry picking) */
- kd.num_denom_keys = kd_old.num_denom_keys;
- kd.last_denom_issue_date = kd_old.last_denom_issue_date;
+ kd.num_denom_keys
+ = kd_old.num_denom_keys;
+ kd.last_denom_issue_date
+ = kd_old.last_denom_issue_date;
GNUNET_array_grow (kd.denom_keys,
kd.denom_keys_size,
kd.num_denom_keys);
/* First make a shallow copy, we then need another pass for the RSA key... */
- memcpy (kd.denom_keys,
- kd_old.denom_keys,
- kd_old.num_denom_keys * sizeof (struct
- TALER_EXCHANGE_DenomPublicKey));
+ GNUNET_memcpy (kd.denom_keys,
+ kd_old.denom_keys,
+ kd_old.num_denom_keys * sizeof (struct
+ TALER_EXCHANGE_DenomPublicKey));
for (unsigned int i = 0; i<kd_old.num_denom_keys; i++)
TALER_denom_pub_deep_copy (&kd.denom_keys[i].key,
@@ -1390,10 +1391,10 @@ keys_completed_cb (void *cls,
GNUNET_array_grow (anew->denom_keys,
anew->num_denom_keys,
aold->num_denom_keys);
- memcpy (anew->denom_keys,
- aold->denom_keys,
- aold->num_denom_keys
- * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
+ GNUNET_memcpy (anew->denom_keys,
+ aold->denom_keys,
+ aold->num_denom_keys
+ * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
}
/* Old auditors got just copied into new ones. */
@@ -1401,11 +1402,11 @@ keys_completed_cb (void *cls,
decode_keys_json (j,
true,
&kd,
- &vc))
+ &kresp.details.ok.compat))
{
TALER_LOG_ERROR ("Could not decode /keys response\n");
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ kresp.hr.http_status = 0;
+ kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
for (unsigned int i = 0; i<kd.num_auditors; i++)
{
struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i];
@@ -1436,13 +1437,13 @@ keys_completed_cb (void *cls,
case MHD_HTTP_NOT_FOUND:
if (NULL == j)
{
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = TALER_ErrorCode_get_hint (hr.ec);
+ kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
}
else
{
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ kresp.hr.ec = TALER_JSON_get_error_code (j);
+ kresp.hr.hint = TALER_JSON_get_error_hint (j);
}
break;
default:
@@ -1450,18 +1451,18 @@ keys_completed_cb (void *cls,
exchange->keys_error_count++;
if (NULL == j)
{
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = TALER_ErrorCode_get_hint (hr.ec);
+ kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
}
else
{
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ kresp.hr.ec = TALER_JSON_get_error_code (j);
+ kresp.hr.hint = TALER_JSON_get_error_hint (j);
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) kresp.hr.ec);
break;
}
exchange->key_data = kd;
@@ -1491,9 +1492,7 @@ keys_completed_cb (void *cls,
free_key_data (&kd_old);
/* notify application that we failed */
exchange->cert_cb (exchange->cert_cb_cls,
- &hr,
- NULL,
- vc);
+ &kresp);
return;
}
@@ -1504,11 +1503,11 @@ keys_completed_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Successfully downloaded exchange's keys\n");
update_auditors (exchange);
+ kresp.details.ok.keys = &exchange->key_data;
+
/* notify application about the key information */
exchange->cert_cb (exchange->cert_cb_cls,
- &hr,
- &exchange->key_data,
- vc);
+ &kresp);
free_key_data (&kd_old);
}
@@ -1702,7 +1701,6 @@ static void
deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
const json_t *data)
{
- enum TALER_EXCHANGE_VersionCompatibility vc;
json_t *keys;
const char *url;
uint32_t version;
@@ -1719,10 +1717,11 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_JSON_spec_end ()
};
struct TALER_EXCHANGE_Keys key_data;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .ec = TALER_EC_NONE,
- .http_status = MHD_HTTP_OK,
- .reply = data
+ struct TALER_EXCHANGE_KeysResponse kresp = {
+ .hr.ec = TALER_EC_NONE,
+ .hr.http_status = MHD_HTTP_OK,
+ .hr.reply = data,
+ .details.ok.keys = &exchange->key_data
};
if (NULL == data)
@@ -1754,7 +1753,7 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
decode_keys_json (keys,
false,
&key_data,
- &vc))
+ &kresp.details.ok.compat))
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
@@ -1771,9 +1770,7 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
update_auditors (exchange);
/* notify application about the key information */
exchange->cert_cb (exchange->cert_cb_cls,
- &hr,
- &exchange->key_data,
- vc);
+ &kresp);
GNUNET_JSON_parse_free (spec);
}
@@ -2056,17 +2053,18 @@ request_keys (void *cls)
url);
if (NULL == kr->url)
{
- struct TALER_EXCHANGE_HttpResponse hr = {
- .ec = TALER_EC_GENERIC_CONFIGURATION_INVALID
+ struct TALER_EXCHANGE_KeysResponse kresp = {
+ .hr.ec = TALER_EC_GENERIC_CONFIGURATION_INVALID,
+ /* Next line is technically unnecessary, as the
+ http status we set is 0 */
+ .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR
};
GNUNET_free (kr);
exchange->keys_error_count++;
exchange->state = MHS_FAILED;
exchange->cert_cb (exchange->cert_cb_cls,
- &hr,
- NULL,
- TALER_EXCHANGE_VC_PROTOCOL_ERROR);
+ &kresp);
return;
}
diff --git a/src/lib/exchange_api_kyc_check.c b/src/lib/exchange_api_kyc_check.c
index 2f03730a8..d1580e76c 100644
--- a/src/lib/exchange_api_kyc_check.c
+++ b/src/lib/exchange_api_kyc_check.c
@@ -100,11 +100,11 @@ handle_kyc_check_finished (void *cls,
uint32_t status;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
- &ks.details.success.exchange_sig),
+ &ks.details.ok.exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
- &ks.details.success.exchange_pub),
+ &ks.details.ok.exchange_pub),
GNUNET_JSON_spec_timestamp ("now",
- &ks.details.success.timestamp),
+ &ks.details.ok.timestamp),
GNUNET_JSON_spec_json ("kyc_details",
&kyc_details),
GNUNET_JSON_spec_uint32 ("aml_status",
@@ -123,13 +123,13 @@ handle_kyc_check_finished (void *cls,
ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
- ks.details.success.kyc_details = kyc_details;
- ks.details.success.aml_status
+ ks.details.ok.kyc_details = kyc_details;
+ ks.details.ok.aml_status
= (enum TALER_AmlDecisionState) status;
key_state = TALER_EXCHANGE_get_keys (kch->exchange);
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_state,
- &ks.details.success.exchange_pub))
+ &ks.details.ok.exchange_pub))
{
GNUNET_break_op (0);
ks.http_status = 0;
@@ -141,10 +141,10 @@ handle_kyc_check_finished (void *cls,
if (GNUNET_OK !=
TALER_exchange_online_account_setup_success_verify (
&kch->h_payto,
- ks.details.success.kyc_details,
- ks.details.success.timestamp,
- &ks.details.success.exchange_pub,
- &ks.details.success.exchange_sig))
+ ks.details.ok.kyc_details,
+ ks.details.ok.timestamp,
+ &ks.details.ok.exchange_pub,
+ &ks.details.ok.exchange_sig))
{
GNUNET_break_op (0);
ks.http_status = 0;
diff --git a/src/lib/exchange_api_link.c b/src/lib/exchange_api_link.c
index 9e8625ed5..3b998f23a 100644
--- a/src/lib/exchange_api_link.c
+++ b/src/lib/exchange_api_link.c
@@ -365,8 +365,8 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
if (off_coin == num_coins)
{
- lr.details.success.num_coins = num_coins;
- lr.details.success.coins = lcis;
+ lr.details.ok.num_coins = num_coins;
+ lr.details.ok.coins = lcis;
lh->link_cb (lh->link_cb_cls,
&lr);
lh->link_cb = NULL;
diff --git a/src/lib/exchange_api_lookup_aml_decision.c b/src/lib/exchange_api_lookup_aml_decision.c
index 897a21f10..9e20c83b8 100644
--- a/src/lib/exchange_api_lookup_aml_decision.c
+++ b/src/lib/exchange_api_lookup_aml_decision.c
@@ -190,17 +190,17 @@ parse_decision_ok (struct TALER_EXCHANGE_LookupAmlDecision *lh,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
- lr.details.success.aml_history_length = json_array_size (aml_history);
- lr.details.success.kyc_attributes_length = json_array_size (kyc_attributes);
+ lr.details.ok.aml_history_length = json_array_size (aml_history);
+ lr.details.ok.kyc_attributes_length = json_array_size (kyc_attributes);
{
struct TALER_EXCHANGE_AmlDecisionDetail aml_history_ar[
- GNUNET_NZL (lr.details.success.aml_history_length)];
+ GNUNET_NZL (lr.details.ok.aml_history_length)];
struct TALER_EXCHANGE_KycHistoryDetail kyc_attributes_ar[
- lr.details.success.kyc_attributes_length];
+ GNUNET_NZL (lr.details.ok.kyc_attributes_length)];
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
- lr.details.success.aml_history = aml_history_ar;
- lr.details.success.kyc_attributes = kyc_attributes_ar;
+ lr.details.ok.aml_history = aml_history_ar;
+ lr.details.ok.kyc_attributes = kyc_attributes_ar;
ret = parse_aml_history (aml_history,
aml_history_ar);
if (GNUNET_OK == ret)
@@ -256,6 +256,8 @@ handle_lookup_finished (void *cls,
GNUNET_assert (NULL == lh->decision_cb);
TALER_EXCHANGE_lookup_aml_decision_cancel (lh);
return;
+ case MHD_HTTP_NO_CONTENT:
+ break;
case MHD_HTTP_BAD_REQUEST:
lr.hr.ec = TALER_JSON_get_error_code (j);
lr.hr.hint = TALER_JSON_get_error_hint (j);
diff --git a/src/lib/exchange_api_lookup_aml_decisions.c b/src/lib/exchange_api_lookup_aml_decisions.c
index f8a1d7fa3..403acb54a 100644
--- a/src/lib/exchange_api_lookup_aml_decisions.c
+++ b/src/lib/exchange_api_lookup_aml_decisions.c
@@ -139,13 +139,13 @@ parse_decisions_ok (struct TALER_EXCHANGE_LookupAmlDecisions *lh,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
- lr.details.success.decisions_length = json_array_size (records);
+ lr.details.ok.decisions_length = json_array_size (records);
{
struct TALER_EXCHANGE_AmlDecisionSummary decisions[
- GNUNET_NZL (lr.details.success.decisions_length)];
+ GNUNET_NZL (lr.details.ok.decisions_length)];
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
- lr.details.success.decisions = decisions;
+ lr.details.ok.decisions = decisions;
ret = parse_aml_decisions (records,
decisions);
if (GNUNET_OK == ret)
diff --git a/src/lib/exchange_api_management_drain_profits.c b/src/lib/exchange_api_management_drain_profits.c
index 9cf1af85e..bc7232b87 100644
--- a/src/lib/exchange_api_management_drain_profits.c
+++ b/src/lib/exchange_api_management_drain_profits.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2020-2022 Taler Systems SA
+ Copyright (C) 2020-2023 Taler Systems SA
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
@@ -79,9 +79,9 @@ handle_drain_profits_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementDrainProfitsHandle *dp = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementDrainResponse dr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
dp->job = NULL;
@@ -90,32 +90,32 @@ handle_drain_profits_finished (void *cls,
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ dr.hr.ec = TALER_JSON_get_error_code (json);
+ dr.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ dr.hr.ec = TALER_JSON_get_error_code (json);
+ dr.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_PRECONDITION_FAILED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ dr.hr.ec = TALER_JSON_get_error_code (json);
+ dr.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ dr.hr.ec = TALER_JSON_get_error_code (json);
+ dr.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management drain profits\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) dr.hr.ec);
break;
}
if (NULL != dp->cb)
{
dp->cb (dp->cb_cls,
- &hr);
+ &dr);
dp->cb = NULL;
}
TALER_EXCHANGE_management_drain_profits_cancel (dp);
diff --git a/src/lib/exchange_api_management_get_keys.c b/src/lib/exchange_api_management_get_keys.c
index 80c471441..c649f397f 100644
--- a/src/lib/exchange_api_management_get_keys.c
+++ b/src/lib/exchange_api_management_get_keys.c
@@ -75,11 +75,16 @@ struct TALER_EXCHANGE_ManagementGetKeysHandle
* @param response the response
* @return #GNUNET_OK if the response was well-formed
*/
-static int
+static enum GNUNET_GenericReturnValue
handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
const json_t *response)
{
- struct TALER_EXCHANGE_FutureKeys fk;
+ struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
+ .hr.http_status = MHD_HTTP_OK,
+ .hr.reply = response,
+ };
+ struct TALER_EXCHANGE_FutureKeys *fk
+ = &gkr.details.ok.keys;
json_t *sk;
json_t *dk;
bool ok;
@@ -89,13 +94,13 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
GNUNET_JSON_spec_json ("future_signkeys",
&sk),
GNUNET_JSON_spec_fixed_auto ("master_pub",
- &fk.master_pub),
+ &fk->master_pub),
GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
- &fk.denom_secmod_public_key),
+ &fk->denom_secmod_public_key),
GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
- &fk.denom_secmod_cs_public_key),
+ &fk->denom_secmod_cs_public_key),
GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
- &fk.signkey_secmod_public_key),
+ &fk->signkey_secmod_public_key),
GNUNET_JSON_spec_end ()
};
@@ -107,21 +112,21 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
- fk.num_sign_keys = json_array_size (sk);
- fk.num_denom_keys = json_array_size (dk);
- fk.sign_keys = GNUNET_new_array (
- fk.num_sign_keys,
+ fk->num_sign_keys = json_array_size (sk);
+ fk->num_denom_keys = json_array_size (dk);
+ fk->sign_keys = GNUNET_new_array (
+ fk->num_sign_keys,
struct TALER_EXCHANGE_FutureSigningPublicKey);
- fk.denom_keys = GNUNET_new_array (
- fk.num_denom_keys,
+ fk->denom_keys = GNUNET_new_array (
+ fk->num_denom_keys,
struct TALER_EXCHANGE_FutureDenomPublicKey);
ok = true;
- for (unsigned int i = 0; i<fk.num_sign_keys; i++)
+ for (unsigned int i = 0; i<fk->num_sign_keys; i++)
{
json_t *j = json_array_get (sk,
i);
struct TALER_EXCHANGE_FutureSigningPublicKey *sign_key
- = &fk.sign_keys[i];
+ = &fk->sign_keys[i];
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("key",
&sign_key->key),
@@ -155,7 +160,7 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
&sign_key->key,
sign_key->valid_from,
duration,
- &fk.signkey_secmod_public_key,
+ &fk->signkey_secmod_public_key,
&sign_key->signkey_secmod_sig))
{
GNUNET_break_op (0);
@@ -164,12 +169,12 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
}
}
}
- for (unsigned int i = 0; i<fk.num_denom_keys; i++)
+ for (unsigned int i = 0; i<fk->num_denom_keys; i++)
{
json_t *j = json_array_get (dk,
i);
struct TALER_EXCHANGE_FutureDenomPublicKey *denom_key
- = &fk.denom_keys[i];
+ = &fk->denom_keys[i];
const char *section_name;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any ("value",
@@ -236,7 +241,7 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
section_name,
denom_key->valid_from,
duration,
- &fk.denom_secmod_public_key,
+ &fk->denom_secmod_public_key,
&denom_key->denom_secmod_sig))
{
GNUNET_break_op (0);
@@ -256,7 +261,7 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
section_name,
denom_key->valid_from,
duration,
- &fk.denom_secmod_cs_public_key,
+ &fk->denom_secmod_cs_public_key,
&denom_key->denom_secmod_sig))
{
GNUNET_break_op (0);
@@ -277,19 +282,13 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
}
if (ok)
{
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = MHD_HTTP_OK,
- .reply = response
- };
-
gh->cb (gh->cb_cls,
- &hr,
- &fk);
+ &gkr);
}
- for (unsigned int i = 0; i<fk.num_denom_keys; i++)
- TALER_denom_pub_free (&fk.denom_keys[i].key);
- GNUNET_free (fk.sign_keys);
- GNUNET_free (fk.denom_keys);
+ for (unsigned int i = 0; i<fk->num_denom_keys; i++)
+ TALER_denom_pub_free (&fk->denom_keys[i].key);
+ GNUNET_free (fk->sign_keys);
+ GNUNET_free (fk->denom_keys);
GNUNET_JSON_parse_free (spec);
return (ok) ? GNUNET_OK : GNUNET_SYSERR;
}
@@ -310,9 +309,9 @@ handle_get_keys_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementGetKeysHandle *gh = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
gh->job = NULL;
@@ -334,25 +333,24 @@ handle_get_keys_finished (void *cls,
/* unexpected response code */
if (NULL != json)
{
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ gkr.hr.ec = TALER_JSON_get_error_code (json);
+ gkr.hr.hint = TALER_JSON_get_error_hint (json);
}
else
{
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = TALER_ErrorCode_get_hint (hr.ec);
+ gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec);
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management get keys\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) gkr.hr.ec);
break;
}
if (NULL != gh->cb)
{
gh->cb (gh->cb_cls,
- &hr,
- NULL);
+ &gkr);
gh->cb = NULL;
}
TALER_EXCHANGE_get_management_keys_cancel (gh);
diff --git a/src/lib/exchange_api_management_post_extensions.c b/src/lib/exchange_api_management_post_extensions.c
index abec4ef09..b9721a982 100644
--- a/src/lib/exchange_api_management_post_extensions.c
+++ b/src/lib/exchange_api_management_post_extensions.c
@@ -83,9 +83,9 @@ handle_post_extensions_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementPostExtensionsResponse per = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
ph->job = NULL;
@@ -94,28 +94,28 @@ handle_post_extensions_finished (void *cls,
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ per.hr.ec = TALER_JSON_get_error_code (json);
+ per.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ per.hr.ec = TALER_JSON_get_error_code (json);
+ per.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ per.hr.ec = TALER_JSON_get_error_code (json);
+ per.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management post extensions\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) per.hr.ec);
break;
}
if (NULL != ph->cb)
{
ph->cb (ph->cb_cls,
- &hr);
+ &per);
ph->cb = NULL;
}
TALER_EXCHANGE_management_post_extensions_cancel (ph);
diff --git a/src/lib/exchange_api_management_post_keys.c b/src/lib/exchange_api_management_post_keys.c
index 291c0ac02..a46124d90 100644
--- a/src/lib/exchange_api_management_post_keys.c
+++ b/src/lib/exchange_api_management_post_keys.c
@@ -82,9 +82,9 @@ handle_post_keys_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementPostKeysHandle *ph = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementPostKeysResponse pkr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
ph->job = NULL;
@@ -93,32 +93,32 @@ handle_post_keys_finished (void *cls,
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ pkr.hr.ec = TALER_JSON_get_error_code (json);
+ pkr.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ pkr.hr.ec = TALER_JSON_get_error_code (json);
+ pkr.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ pkr.hr.ec = TALER_JSON_get_error_code (json);
+ pkr.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ pkr.hr.ec = TALER_JSON_get_error_code (json);
+ pkr.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management post keys\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) pkr.hr.ec);
break;
}
if (NULL != ph->cb)
{
ph->cb (ph->cb_cls,
- &hr);
+ &pkr);
ph->cb = NULL;
}
TALER_EXCHANGE_post_management_keys_cancel (ph);
diff --git a/src/lib/exchange_api_management_revoke_denomination_key.c b/src/lib/exchange_api_management_revoke_denomination_key.c
index 8d65b6451..aa4d527a7 100644
--- a/src/lib/exchange_api_management_revoke_denomination_key.c
+++ b/src/lib/exchange_api_management_revoke_denomination_key.c
@@ -82,9 +82,9 @@ handle_revoke_denomination_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *rh = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementRevokeDenominationResponse rdr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
rh->job = NULL;
@@ -92,30 +92,30 @@ handle_revoke_denomination_finished (void *cls,
{
case 0:
/* no reply */
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = "server offline?";
+ rdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ rdr.hr.hint = "server offline?";
break;
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ rdr.hr.ec = TALER_JSON_get_error_code (json);
+ rdr.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ rdr.hr.ec = TALER_JSON_get_error_code (json);
+ rdr.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management revoke denomination\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) rdr.hr.ec);
break;
}
if (NULL != rh->cb)
{
rh->cb (rh->cb_cls,
- &hr);
+ &rdr);
rh->cb = NULL;
}
TALER_EXCHANGE_management_revoke_denomination_key_cancel (rh);
diff --git a/src/lib/exchange_api_management_revoke_signing_key.c b/src/lib/exchange_api_management_revoke_signing_key.c
index aac27678f..c4d634248 100644
--- a/src/lib/exchange_api_management_revoke_signing_key.c
+++ b/src/lib/exchange_api_management_revoke_signing_key.c
@@ -79,9 +79,9 @@ handle_revoke_signing_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *rh = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse rsr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
rh->job = NULL;
@@ -89,30 +89,30 @@ handle_revoke_signing_finished (void *cls,
{
case 0:
/* no reply */
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = "server offline?";
+ rsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ rsr.hr.hint = "server offline?";
break;
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ rsr.hr.ec = TALER_JSON_get_error_code (json);
+ rsr.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ rsr.hr.ec = TALER_JSON_get_error_code (json);
+ rsr.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management revoke signkey\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) rsr.hr.ec);
break;
}
if (NULL != rh->cb)
{
rh->cb (rh->cb_cls,
- &hr);
+ &rsr);
rh->cb = NULL;
}
TALER_EXCHANGE_management_revoke_signing_key_cancel (rh);
diff --git a/src/lib/exchange_api_management_update_aml_officer.c b/src/lib/exchange_api_management_update_aml_officer.c
index 6e1669465..0033a1308 100644
--- a/src/lib/exchange_api_management_update_aml_officer.c
+++ b/src/lib/exchange_api_management_update_aml_officer.c
@@ -79,9 +79,9 @@ handle_update_aml_officer_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *wh = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse uar = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
wh->job = NULL;
@@ -89,34 +89,34 @@ handle_update_aml_officer_finished (void *cls,
{
case 0:
/* no reply */
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = "server offline?";
+ uar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ uar.hr.hint = "server offline?";
break;
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ uar.hr.ec = TALER_JSON_get_error_code (json);
+ uar.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ uar.hr.ec = TALER_JSON_get_error_code (json);
+ uar.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ uar.hr.ec = TALER_JSON_get_error_code (json);
+ uar.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management update AML officer\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) uar.hr.ec);
break;
}
if (NULL != wh->cb)
{
wh->cb (wh->cb_cls,
- &hr);
+ &uar);
wh->cb = NULL;
}
TALER_EXCHANGE_management_update_aml_officer_cancel (wh);
diff --git a/src/lib/exchange_api_management_wire_disable.c b/src/lib/exchange_api_management_wire_disable.c
index 5d97eef75..30a010762 100644
--- a/src/lib/exchange_api_management_wire_disable.c
+++ b/src/lib/exchange_api_management_wire_disable.c
@@ -79,9 +79,9 @@ handle_auditor_disable_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementWireDisableHandle *wh = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementWireDisableResponse wdr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
wh->job = NULL;
@@ -89,38 +89,38 @@ handle_auditor_disable_finished (void *cls,
{
case 0:
/* no reply */
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = "server offline?";
+ wdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ wdr.hr.hint = "server offline?";
break;
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ wdr.hr.ec = TALER_JSON_get_error_code (json);
+ wdr.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ wdr.hr.ec = TALER_JSON_get_error_code (json);
+ wdr.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ wdr.hr.ec = TALER_JSON_get_error_code (json);
+ wdr.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ wdr.hr.ec = TALER_JSON_get_error_code (json);
+ wdr.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d exchange management disable wire\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) wdr.hr.ec);
break;
}
if (NULL != wh->cb)
{
wh->cb (wh->cb_cls,
- &hr);
+ &wdr);
wh->cb = NULL;
}
TALER_EXCHANGE_management_disable_wire_cancel (wh);
diff --git a/src/lib/exchange_api_management_wire_enable.c b/src/lib/exchange_api_management_wire_enable.c
index 55480474e..23a98b153 100644
--- a/src/lib/exchange_api_management_wire_enable.c
+++ b/src/lib/exchange_api_management_wire_enable.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015-2021 Taler Systems SA
+ Copyright (C) 2015-2023 Taler Systems SA
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
@@ -79,9 +79,9 @@ handle_auditor_enable_finished (void *cls,
{
struct TALER_EXCHANGE_ManagementWireEnableHandle *wh = cls;
const json_t *json = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_EXCHANGE_ManagementWireEnableResponse wer = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
wh->job = NULL;
@@ -89,34 +89,34 @@ handle_auditor_enable_finished (void *cls,
{
case 0:
/* no reply */
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = "server offline?";
+ wer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ wer.hr.hint = "server offline?";
break;
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ wer.hr.ec = TALER_JSON_get_error_code (json);
+ wer.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ wer.hr.ec = TALER_JSON_get_error_code (json);
+ wer.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ wer.hr.ec = TALER_JSON_get_error_code (json);
+ wer.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management enable wire\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) wer.hr.ec);
break;
}
if (NULL != wh->cb)
{
wh->cb (wh->cb_cls,
- &hr);
+ &wer);
wh->cb = NULL;
}
TALER_EXCHANGE_management_enable_wire_cancel (wh);
@@ -128,6 +128,9 @@ TALER_EXCHANGE_management_enable_wire (
struct GNUNET_CURL_Context *ctx,
const char *url,
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp validity_start,
const struct TALER_MasterSignatureP *master_sig1,
const struct TALER_MasterSignatureP *master_sig2,
@@ -167,6 +170,13 @@ TALER_EXCHANGE_management_enable_wire (
body = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("payto_uri",
payto_uri),
+ GNUNET_JSON_pack_array_incref ("debit_restrictions",
+ (json_t *) debit_restrictions),
+ GNUNET_JSON_pack_array_incref ("credit_restrictions",
+ (json_t *) credit_restrictions),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("conversion_url",
+ conversion_url)),
GNUNET_JSON_pack_data_auto ("master_sig_add",
master_sig1),
GNUNET_JSON_pack_data_auto ("master_sig_wire",
diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c
index feeef4001..3a8144a3c 100644
--- a/src/lib/exchange_api_melt.c
+++ b/src/lib/exchange_api_melt.c
@@ -221,16 +221,16 @@ handle_melt_finished (void *cls,
if (GNUNET_OK !=
verify_melt_signature_ok (mh,
j,
- &mr.details.success.sign_key))
+ &mr.details.ok.sign_key))
{
GNUNET_break_op (0);
mr.hr.http_status = 0;
mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
break;
}
- mr.details.success.noreveal_index = mh->noreveal_index;
- mr.details.success.num_mbds = mh->rd->fresh_pks_len;
- mr.details.success.mbds = mh->mbds;
+ mr.details.ok.noreveal_index = mh->noreveal_index;
+ mr.details.ok.num_mbds = mh->rd->fresh_pks_len;
+ mr.details.ok.mbds = mh->mbds;
mh->melt_cb (mh->melt_cb_cls,
&mr);
mh->melt_cb = NULL;
@@ -478,7 +478,7 @@ csr_cb (void *cls,
break;
case TALER_DENOMINATION_CS:
GNUNET_assert (TALER_DENOMINATION_CS == wv->cipher);
- *wv = csrr->details.success.alg_values[nks_off];
+ *wv = csrr->details.ok.alg_values[nks_off];
nks_off++;
break;
}
diff --git a/src/lib/exchange_api_purse_deposit.c b/src/lib/exchange_api_purse_deposit.c
index f5a503b0b..f88d23299 100644
--- a/src/lib/exchange_api_purse_deposit.c
+++ b/src/lib/exchange_api_purse_deposit.c
@@ -164,17 +164,17 @@ handle_purse_deposit_finished (void *cls,
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&exchange_pub),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
- &dr.details.success.h_contract_terms),
+ &dr.details.ok.h_contract_terms),
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
&etime),
GNUNET_JSON_spec_timestamp ("purse_expiration",
- &dr.details.success.purse_expiration),
+ &dr.details.ok.purse_expiration),
TALER_JSON_spec_amount ("total_deposited",
keys->currency,
- &dr.details.success.total_deposited),
+ &dr.details.ok.total_deposited),
TALER_JSON_spec_amount ("purse_value_after_fees",
keys->currency,
- &dr.details.success.purse_value_after_fees),
+ &dr.details.ok.purse_value_after_fees),
GNUNET_JSON_spec_end ()
};
@@ -200,11 +200,11 @@ handle_purse_deposit_finished (void *cls,
if (GNUNET_OK !=
TALER_exchange_online_purse_created_verify (
etime,
- dr.details.success.purse_expiration,
- &dr.details.success.purse_value_after_fees,
- &dr.details.success.total_deposited,
+ dr.details.ok.purse_expiration,
+ &dr.details.ok.purse_value_after_fees,
+ &dr.details.ok.total_deposited,
&pch->purse_pub,
- &dr.details.success.h_contract_terms,
+ &dr.details.ok.h_contract_terms,
&exchange_pub,
&exchange_sig))
{
diff --git a/src/lib/exchange_api_purse_merge.c b/src/lib/exchange_api_purse_merge.c
index fd11978d9..6e1995e0d 100644
--- a/src/lib/exchange_api_purse_merge.c
+++ b/src/lib/exchange_api_purse_merge.c
@@ -152,11 +152,11 @@ handle_purse_merge_finished (void *cls,
struct TALER_Amount total_deposited;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
- &dr.details.success.exchange_sig),
+ &dr.details.ok.exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
- &dr.details.success.exchange_pub),
+ &dr.details.ok.exchange_pub),
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
- &dr.details.success.etime),
+ &dr.details.ok.etime),
TALER_JSON_spec_amount ("merge_amount",
pch->purse_value_after_fees.currency,
&total_deposited),
@@ -176,7 +176,7 @@ handle_purse_merge_finished (void *cls,
key_state = TALER_EXCHANGE_get_keys (pch->exchange);
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_state,
- &dr.details.success.exchange_pub))
+ &dr.details.ok.exchange_pub))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
@@ -185,15 +185,15 @@ handle_purse_merge_finished (void *cls,
}
if (GNUNET_OK !=
TALER_exchange_online_purse_merged_verify (
- dr.details.success.etime,
+ dr.details.ok.etime,
pch->purse_expiration,
&pch->purse_value_after_fees,
&pch->purse_pub,
&pch->h_contract_terms,
&pch->reserve_pub,
pch->provider_url,
- &dr.details.success.exchange_pub,
- &dr.details.success.exchange_sig))
+ &dr.details.ok.exchange_pub,
+ &dr.details.ok.exchange_sig))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
diff --git a/src/lib/exchange_api_purses_get.c b/src/lib/exchange_api_purses_get.c
index 5d63ecf13..4c2fdd79c 100644
--- a/src/lib/exchange_api_purses_get.c
+++ b/src/lib/exchange_api_purses_get.c
@@ -101,16 +101,16 @@ handle_purse_get_finished (void *cls,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_timestamp ("merge_timestamp",
- &dr.details.success.merge_timestamp),
+ &dr.details.ok.merge_timestamp),
&no_merge),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_timestamp ("deposit_timestamp",
- &dr.details.success.deposit_timestamp),
+ &dr.details.ok.deposit_timestamp),
&no_deposit),
TALER_JSON_spec_amount_any ("balance",
- &dr.details.success.balance),
+ &dr.details.ok.balance),
GNUNET_JSON_spec_timestamp ("purse_expiration",
- &dr.details.success.purse_expiration),
+ &dr.details.ok.purse_expiration),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&exchange_pub),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
@@ -142,9 +142,9 @@ handle_purse_get_finished (void *cls,
}
if (GNUNET_OK !=
TALER_exchange_online_purse_status_verify (
- dr.details.success.merge_timestamp,
- dr.details.success.deposit_timestamp,
- &dr.details.success.balance,
+ dr.details.ok.merge_timestamp,
+ dr.details.ok.deposit_timestamp,
+ &dr.details.ok.balance,
&exchange_pub,
&exchange_sig))
{
diff --git a/src/lib/exchange_api_recoup.c b/src/lib/exchange_api_recoup.c
index 8de4da123..b89bda8b3 100644
--- a/src/lib/exchange_api_recoup.c
+++ b/src/lib/exchange_api_recoup.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2017-2022 Taler Systems SA
+ Copyright (C) 2017-2023 Taler Systems SA
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
@@ -100,16 +100,15 @@ static enum GNUNET_GenericReturnValue
process_recoup_response (const struct TALER_EXCHANGE_RecoupHandle *ph,
const json_t *json)
{
- struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_EXCHANGE_RecoupResponse rr = {
+ .hr.reply = json,
+ .hr.http_status = MHD_HTTP_OK
+ };
struct GNUNET_JSON_Specification spec_withdraw[] = {
GNUNET_JSON_spec_fixed_auto ("reserve_pub",
- &reserve_pub),
+ &rr.details.ok.reserve_pub),
GNUNET_JSON_spec_end ()
};
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = json,
- .http_status = MHD_HTTP_OK
- };
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
@@ -120,8 +119,7 @@ process_recoup_response (const struct TALER_EXCHANGE_RecoupHandle *ph,
return GNUNET_SYSERR;
}
ph->cb (ph->cb_cls,
- &hr,
- &reserve_pub);
+ &rr);
return GNUNET_OK;
}
@@ -141,9 +139,9 @@ handle_recoup_finished (void *cls,
{
struct TALER_EXCHANGE_RecoupHandle *ph = cls;
const json_t *j = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_RecoupResponse rr = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
};
const struct TALER_EXCHANGE_Keys *keys;
@@ -152,7 +150,7 @@ handle_recoup_finished (void *cls,
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
@@ -160,8 +158,8 @@ handle_recoup_finished (void *cls,
j))
{
GNUNET_break_op (0);
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- hr.http_status = 0;
+ rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ rr.hr.http_status = 0;
break;
}
TALER_EXCHANGE_recoup_cancel (ph);
@@ -169,22 +167,22 @@ handle_recoup_finished (void *cls,
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
{
struct TALER_Amount min_key;
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
if (GNUNET_OK !=
TALER_EXCHANGE_get_min_denomination_ (keys,
&min_key))
{
GNUNET_break (0);
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- hr.http_status = 0;
+ rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ rr.hr.http_status = 0;
break;
}
if (GNUNET_OK !=
@@ -197,8 +195,8 @@ handle_recoup_finished (void *cls,
&min_key))
{
GNUNET_break (0);
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- hr.http_status = 0;
+ rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ rr.hr.http_status = 0;
break;
}
break;
@@ -207,41 +205,40 @@ handle_recoup_finished (void *cls,
/* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_GONE:
/* Kind of normal: the money was already sent to the merchant
(it was too late for the refund). */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange recoup\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) rr.hr.ec);
GNUNET_break (0);
break;
}
ph->cb (ph->cb_cls,
- &hr,
- NULL);
+ &rr);
TALER_EXCHANGE_recoup_cancel (ph);
}
diff --git a/src/lib/exchange_api_recoup_refresh.c b/src/lib/exchange_api_recoup_refresh.c
index 00eeca070..7b42aa7eb 100644
--- a/src/lib/exchange_api_recoup_refresh.c
+++ b/src/lib/exchange_api_recoup_refresh.c
@@ -101,16 +101,15 @@ process_recoup_response (
const struct TALER_EXCHANGE_RecoupRefreshHandle *ph,
const json_t *json)
{
- struct TALER_CoinSpendPublicKeyP old_coin_pub;
+ struct TALER_EXCHANGE_RecoupRefreshResponse rrr = {
+ .hr.reply = json,
+ .hr.http_status = MHD_HTTP_OK
+ };
struct GNUNET_JSON_Specification spec_refresh[] = {
GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
- &old_coin_pub),
+ &rrr.details.ok.old_coin_pub),
GNUNET_JSON_spec_end ()
};
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = json,
- .http_status = MHD_HTTP_OK
- };
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
@@ -121,8 +120,7 @@ process_recoup_response (
return GNUNET_SYSERR;
}
ph->cb (ph->cb_cls,
- &hr,
- &old_coin_pub);
+ &rrr);
return GNUNET_OK;
}
@@ -142,9 +140,9 @@ handle_recoup_refresh_finished (void *cls,
{
struct TALER_EXCHANGE_RecoupRefreshHandle *ph = cls;
const json_t *j = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_RecoupRefreshResponse rrr = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
};
const struct TALER_EXCHANGE_Keys *keys;
@@ -153,7 +151,7 @@ handle_recoup_refresh_finished (void *cls,
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ rrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
@@ -161,8 +159,8 @@ handle_recoup_refresh_finished (void *cls,
j))
{
GNUNET_break_op (0);
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- hr.http_status = 0;
+ rrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ rrr.hr.http_status = 0;
break;
}
TALER_EXCHANGE_recoup_refresh_cancel (ph);
@@ -170,35 +168,35 @@ handle_recoup_refresh_finished (void *cls,
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rrr.hr.ec = TALER_JSON_get_error_code (j);
+ rrr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rrr.hr.ec = TALER_JSON_get_error_code (j);
+ rrr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rrr.hr.ec = TALER_JSON_get_error_code (j);
+ rrr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
{
struct TALER_Amount min_key;
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rrr.hr.ec = TALER_JSON_get_error_code (j);
+ rrr.hr.hint = TALER_JSON_get_error_hint (j);
if (GNUNET_OK !=
TALER_EXCHANGE_get_min_denomination_ (keys,
&min_key))
{
GNUNET_break (0);
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- hr.http_status = 0;
+ rrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ rrr.hr.http_status = 0;
break;
}
if (GNUNET_OK !=
@@ -211,8 +209,8 @@ handle_recoup_refresh_finished (void *cls,
&min_key))
{
GNUNET_break (0);
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- hr.http_status = 0;
+ rrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ rrr.hr.http_status = 0;
break;
}
break;
@@ -220,29 +218,28 @@ handle_recoup_refresh_finished (void *cls,
case MHD_HTTP_GONE:
/* Kind of normal: the money was already sent to the merchant
(it was too late for the refund). */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rrr.hr.ec = TALER_JSON_get_error_code (j);
+ rrr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rrr.hr.ec = TALER_JSON_get_error_code (j);
+ rrr.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rrr.hr.ec = TALER_JSON_get_error_code (j);
+ rrr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange recoup\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) rrr.hr.ec);
GNUNET_break (0);
break;
}
ph->cb (ph->cb_cls,
- &hr,
- NULL);
+ &rrr);
TALER_EXCHANGE_recoup_refresh_cancel (ph);
}
diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c
index cd2a1d1f4..3bdef2020 100644
--- a/src/lib/exchange_api_refreshes_reveal.c
+++ b/src/lib/exchange_api_refreshes_reveal.c
@@ -266,8 +266,8 @@ handle_refresh_reveal_finished (void *cls,
else
{
GNUNET_assert (rrh->noreveal_index < TALER_CNC_KAPPA);
- rr.details.success.num_coins = rrh->md.num_fresh_coins;
- rr.details.success.coins = rcis;
+ rr.details.ok.num_coins = rrh->md.num_fresh_coins;
+ rr.details.ok.coins = rcis;
rrh->reveal_cb (rrh->reveal_cb_cls,
&rr);
rrh->reveal_cb = NULL;
diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c
index 855b4fcc7..7c9616ea5 100644
--- a/src/lib/exchange_api_refund.c
+++ b/src/lib/exchange_api_refund.c
@@ -585,37 +585,28 @@ handle_refund_finished (void *cls,
const void *response)
{
struct TALER_EXCHANGE_RefundHandle *rh = cls;
- struct TALER_ExchangePublicKeyP exchange_pub;
- struct TALER_ExchangeSignatureP exchange_sig;
- struct TALER_ExchangePublicKeyP *ep = NULL;
- struct TALER_ExchangeSignatureP *es = NULL;
const json_t *j = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_RefundResponse rr = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
};
rh->job = NULL;
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
verify_refund_signature_ok (rh,
j,
- &exchange_pub,
- &exchange_sig))
+ &rr.details.ok.exchange_pub,
+ &rr.details.ok.exchange_sig))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_SIGNATURE_BY_EXCHANGE;
- }
- else
- {
- ep = &exchange_pub;
- es = &exchange_sig;
+ rr.hr.http_status = 0;
+ rr.hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_SIGNATURE_BY_EXCHANGE;
}
break;
case MHD_HTTP_BAD_REQUEST:
@@ -623,21 +614,21 @@ handle_refund_finished (void *cls,
(or API version conflict); also can happen if the currency
differs (which we should obviously never support).
Just pass JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
/* Requested total refunds exceed deposited amount */
@@ -649,19 +640,19 @@ handle_refund_finished (void *cls,
json_dumpf (j,
stderr,
JSON_INDENT (2));
- hr.http_status = 0;
- hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE;
- hr.hint = "conflict information provided by exchange is invalid";
+ rr.hr.http_status = 0;
+ rr.hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE;
+ rr.hr.hint = "conflict information provided by exchange is invalid";
break;
}
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_GONE:
/* Kind of normal: the money was already sent to the merchant
(it was too late for the refund). */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_PRECONDITION_FAILED:
if (GNUNET_OK !=
@@ -669,37 +660,35 @@ handle_refund_finished (void *cls,
j))
{
GNUNET_break (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE;
- hr.hint = "failed precondition proof returned by exchange is invalid";
+ rr.hr.http_status = 0;
+ rr.hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE;
+ rr.hr.hint = "failed precondition proof returned by exchange is invalid";
break;
}
/* Two different refund requests were made about the same deposit, but
carrying identical refund transaction ids. */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange refund\n",
(unsigned int) response_code,
- hr.ec);
+ rr.hr.ec);
break;
}
rh->cb (rh->cb_cls,
- &hr,
- ep,
- es);
+ &rr);
TALER_EXCHANGE_refund_cancel (rh);
}
diff --git a/src/lib/exchange_api_transfers_get.c b/src/lib/exchange_api_transfers_get.c
index 06465618c..f00e36ce3 100644
--- a/src/lib/exchange_api_transfers_get.c
+++ b/src/lib/exchange_api_transfers_get.c
@@ -85,24 +85,33 @@ check_transfers_get_response_ok (
const json_t *json)
{
json_t *details_j;
- struct TALER_EXCHANGE_TransferData td;
struct TALER_Amount total_expected;
struct TALER_MerchantPublicKeyP merchant_pub;
+ struct TALER_EXCHANGE_TransfersGetResponse tgr = {
+ .hr.reply = json,
+ .hr.http_status = MHD_HTTP_OK
+ };
+ struct TALER_EXCHANGE_TransferData *td
+ = &tgr.details.ok.td;
struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_amount_any ("total", &td.total_amount),
- TALER_JSON_spec_amount_any ("wire_fee", &td.wire_fee),
- GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
- GNUNET_JSON_spec_fixed_auto ("h_payto", &td.h_payto),
- GNUNET_JSON_spec_timestamp ("execution_time", &td.execution_time),
- GNUNET_JSON_spec_json ("deposits", &details_j),
- GNUNET_JSON_spec_fixed_auto ("exchange_sig", &td.exchange_sig),
- GNUNET_JSON_spec_fixed_auto ("exchange_pub", &td.exchange_pub),
+ TALER_JSON_spec_amount_any ("total",
+ &td->total_amount),
+ TALER_JSON_spec_amount_any ("wire_fee",
+ &td->wire_fee),
+ GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+ &merchant_pub),
+ GNUNET_JSON_spec_fixed_auto ("h_payto",
+ &td->h_payto),
+ GNUNET_JSON_spec_timestamp ("execution_time",
+ &td->execution_time),
+ GNUNET_JSON_spec_json ("deposits",
+ &details_j),
+ GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+ &td->exchange_sig),
+ GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+ &td->exchange_pub),
GNUNET_JSON_spec_end ()
};
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = json,
- .http_status = MHD_HTTP_OK
- };
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
@@ -113,7 +122,7 @@ check_transfers_get_response_ok (
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
- TALER_amount_set_zero (td.total_amount.currency,
+ TALER_amount_set_zero (td->total_amount.currency,
&total_expected))
{
GNUNET_break_op (0);
@@ -123,22 +132,22 @@ check_transfers_get_response_ok (
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (
TALER_EXCHANGE_get_keys (wdh->exchange),
- &td.exchange_pub))
+ &td->exchange_pub))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
- td.details_length = json_array_size (details_j);
+ td->details_length = json_array_size (details_j);
{
struct GNUNET_HashContext *hash_context;
struct TALER_TrackTransferDetails *details;
- details = GNUNET_new_array (td.details_length,
+ details = GNUNET_new_array (td->details_length,
struct TALER_TrackTransferDetails);
- td.details = details;
+ td->details = details;
hash_context = GNUNET_CRYPTO_hash_context_start ();
- for (unsigned int i = 0; i<td.details_length; i++)
+ for (unsigned int i = 0; i<td->details_length; i++)
{
struct TALER_TrackTransferDetails *detail = &details[i];
struct json_t *detail_j = json_array_get (details_j, i);
@@ -180,7 +189,7 @@ check_transfers_get_response_ok (
TALER_exchange_online_wire_deposit_append (
hash_context,
&detail->h_contract_terms,
- td.execution_time,
+ td->execution_time,
&detail->coin_pub,
&detail->coin_value,
&detail->coin_fee);
@@ -193,13 +202,13 @@ check_transfers_get_response_ok (
&h_details);
if (GNUNET_OK !=
TALER_exchange_online_wire_deposit_verify (
- &td.total_amount,
- &td.wire_fee,
+ &td->total_amount,
+ &td->wire_fee,
&merchant_pub,
- &td.h_payto,
+ &td->h_payto,
&h_details,
- &td.exchange_pub,
- &td.exchange_sig))
+ &td->exchange_pub,
+ &td->exchange_sig))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
@@ -211,7 +220,7 @@ check_transfers_get_response_ok (
if (0 >
TALER_amount_subtract (&total_expected,
&total_expected,
- &td.wire_fee))
+ &td->wire_fee))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
@@ -220,7 +229,7 @@ check_transfers_get_response_ok (
}
if (0 !=
TALER_amount_cmp (&total_expected,
- &td.total_amount))
+ &td->total_amount))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
@@ -228,12 +237,10 @@ check_transfers_get_response_ok (
return GNUNET_SYSERR;
}
wdh->cb (wdh->cb_cls,
- &hr,
- &td);
+ &tgr);
GNUNET_free (details);
}
GNUNET_JSON_parse_free (spec);
- TALER_EXCHANGE_transfers_get_cancel (wdh);
return GNUNET_OK;
}
@@ -253,65 +260,67 @@ handle_transfers_get_finished (void *cls,
{
struct TALER_EXCHANGE_TransfersGetHandle *wdh = cls;
const json_t *j = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_TransfersGetResponse tgr = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
};
wdh->job = NULL;
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK ==
check_transfers_get_response_ok (wdh,
j))
+ {
+ TALER_EXCHANGE_transfers_get_cancel (wdh);
return;
+ }
GNUNET_break_op (0);
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- hr.http_status = 0;
+ tgr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ tgr.hr.http_status = 0;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ tgr.hr.ec = TALER_JSON_get_error_code (j);
+ tgr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ tgr.hr.ec = TALER_JSON_get_error_code (j);
+ tgr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Exchange does not know about transaction;
we should pass the reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ tgr.hr.ec = TALER_JSON_get_error_code (j);
+ tgr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ tgr.hr.ec = TALER_JSON_get_error_code (j);
+ tgr.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ tgr.hr.ec = TALER_JSON_get_error_code (j);
+ tgr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for transfers get\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) tgr.hr.ec);
break;
}
wdh->cb (wdh->cb_cls,
- &hr,
- NULL);
+ &tgr);
TALER_EXCHANGE_transfers_get_cancel (wdh);
}
diff --git a/src/lib/exchange_api_wire.c b/src/lib/exchange_api_wire.c
index c23ea62d8..084988058 100644
--- a/src/lib/exchange_api_wire.c
+++ b/src/lib/exchange_api_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2022 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
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
@@ -66,82 +66,65 @@ struct TALER_EXCHANGE_WireHandle
/**
- * List of wire fees by method.
- */
-struct FeeMap
-{
- /**
- * Next entry in list.
- */
- struct FeeMap *next;
-
- /**
- * Wire method this fee structure is for.
- */
- char *method;
-
- /**
- * Array of wire fees, also linked list, but allocated
- * only once.
- */
- struct TALER_EXCHANGE_WireAggregateFees *fee_list;
-};
-
-
-/**
- * Frees @a fm.
+ * Frees @a wfm array.
*
- * @param fm memory to release
+ * @param wfm fee array to release
+ * @param wfm_len length of the @a wfm array
*/
static void
-free_fees (struct FeeMap *fm)
+free_fees (struct TALER_EXCHANGE_WireFeesByMethod *wfm,
+ unsigned int wfm_len)
{
- while (NULL != fm)
+ for (unsigned int i = 0; i<wfm_len; i++)
{
- struct FeeMap *fe = fm->next;
+ struct TALER_EXCHANGE_WireFeesByMethod *wfmi = &wfm[i];
+
+ while (NULL != wfmi->fees_head)
+ {
+ struct TALER_EXCHANGE_WireAggregateFees *fe
+ = wfmi->fees_head;
- GNUNET_free (fm->fee_list);
- GNUNET_free (fm->method);
- GNUNET_free (fm);
- fm = fe;
+ wfmi->fees_head = fe->next;
+ GNUNET_free (fe);
+ }
}
+ GNUNET_free (wfm);
}
/**
- * Parse wire @a fees and return map.
+ * Parse wire @a fees and return array.
*
* @param master_pub master public key to use to check signatures
* @param fees json AggregateTransferFee to parse
+ * @param[out] fees_len set to length of returned array
* @return NULL on error
*/
-static struct FeeMap *
+static struct TALER_EXCHANGE_WireFeesByMethod *
parse_fees (const struct TALER_MasterPublicKeyP *master_pub,
- json_t *fees)
+ const json_t *fees,
+ unsigned int *fees_len)
{
- struct FeeMap *fm = NULL;
+ struct TALER_EXCHANGE_WireFeesByMethod *fbm;
+ unsigned int fbml = json_object_size (fees);
+ unsigned int i = 0;
const char *key;
- json_t *fee_array;
+ const json_t *fee_array;
- json_object_foreach (fees, key, fee_array) {
- struct FeeMap *fe = GNUNET_new (struct FeeMap);
- unsigned int len;
+ fbm = GNUNET_new_array (fbml,
+ struct TALER_EXCHANGE_WireFeesByMethod);
+ *fees_len = fbml;
+ json_object_foreach ((json_t *) fees, key, fee_array) {
+ struct TALER_EXCHANGE_WireFeesByMethod *fe = &fbm[i++];
unsigned int idx;
json_t *fee;
- if (0 == (len = json_array_size (fee_array)))
- {
- GNUNET_free (fe);
- continue; /* skip */
- }
- fe->method = GNUNET_strdup (key);
- fe->next = fm;
- fe->fee_list = GNUNET_new_array (len,
- struct TALER_EXCHANGE_WireAggregateFees);
- fm = fe;
+ fe->method = key;
+ fe->fees_head = NULL;
json_array_foreach (fee_array, idx, fee)
{
- struct TALER_EXCHANGE_WireAggregateFees *wa = &fe->fee_list[idx];
+ struct TALER_EXCHANGE_WireAggregateFees *wa
+ = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("sig",
&wa->master_sig),
@@ -156,6 +139,8 @@ parse_fees (const struct TALER_MasterPublicKeyP *master_pub,
GNUNET_JSON_spec_end ()
};
+ wa->next = fe->fees_head;
+ fe->fees_head = wa;
if (GNUNET_OK !=
GNUNET_JSON_parse (fee,
spec,
@@ -163,7 +148,8 @@ parse_fees (const struct TALER_MasterPublicKeyP *master_pub,
NULL))
{
GNUNET_break_op (0);
- free_fees (fm);
+ free_fees (fbm,
+ i);
return NULL;
}
if (GNUNET_OK !=
@@ -176,35 +162,14 @@ parse_fees (const struct TALER_MasterPublicKeyP *master_pub,
&wa->master_sig))
{
GNUNET_break_op (0);
- free_fees (fm);
+ free_fees (fbm,
+ i);
return NULL;
}
- if (idx + 1 < len)
- wa->next = &fe->fee_list[idx + 1];
- else
- wa->next = NULL;
- }
- }
- return fm;
-}
-
-
-/**
- * Find fee by @a method.
- *
- * @param fm map to look in
- * @param method key to look for
- * @return NULL if fee is not specified in @a fm
- */
-static const struct TALER_EXCHANGE_WireAggregateFees *
-lookup_fee (const struct FeeMap *fm,
- const char *method)
-{
- for (; NULL != fm; fm = fm->next)
- if (0 == strcasecmp (fm->method,
- method))
- return fm->fee_list;
- return NULL;
+ } /* for all fees over time */
+ } /* for all methods */
+ GNUNET_assert (i == fbml);
+ return fbm;
}
@@ -223,9 +188,9 @@ handle_wire_finished (void *cls,
{
struct TALER_EXCHANGE_WireHandle *wh = cls;
const json_t *j = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_WireResponse wr = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
};
TALER_LOG_DEBUG ("Checking raw /wire response\n");
@@ -233,28 +198,29 @@ handle_wire_finished (void *cls,
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ wr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
wh->exchange->wire_error_count++;
break;
case MHD_HTTP_OK:
{
- json_t *accounts;
- json_t *fees;
- unsigned int num_accounts;
- struct FeeMap *fm;
+ const json_t *accounts;
+ const json_t *fees;
+ const json_t *wads;
+ struct TALER_EXCHANGE_WireFeesByMethod *fbm;
struct TALER_MasterPublicKeyP master_pub;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("master_public_key",
&master_pub),
- GNUNET_JSON_spec_json ("accounts",
- &accounts),
- GNUNET_JSON_spec_json ("fees",
- &fees),
+ GNUNET_JSON_spec_array_const ("accounts",
+ &accounts),
+ GNUNET_JSON_spec_object_const ("fees",
+ &fees),
+ GNUNET_JSON_spec_array_const ("wads",
+ &wads),
GNUNET_JSON_spec_end ()
};
wh->exchange->wire_error_count = 0;
-
if (GNUNET_OK !=
GNUNET_JSON_parse (j,
spec,
@@ -262,8 +228,8 @@ handle_wire_finished (void *cls,
{
/* bogus reply */
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wr.hr.http_status = 0;
+ wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
{
@@ -275,142 +241,100 @@ handle_wire_finished (void *cls,
{
/* bogus reply: master public key in /wire differs from that in /keys */
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wr.hr.http_status = 0;
+ wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
}
- if (0 == (num_accounts = json_array_size (accounts)))
+ wr.details.ok.accounts_len
+ = json_array_size (accounts);
+ if (0 == wr.details.ok.accounts_len)
{
/* bogus reply */
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wr.hr.http_status = 0;
+ wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
- if (NULL == (fm = parse_fees (&master_pub,
- fees)))
+ fbm = parse_fees (&master_pub,
+ fees,
+ &wr.details.ok.fees_len);
+ wr.details.ok.fees = fbm;
+ if (NULL == fbm)
{
/* bogus reply */
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wr.hr.http_status = 0;
+ wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
/* parse accounts */
{
- struct TALER_EXCHANGE_WireAccount was[num_accounts];
-
- for (unsigned int i = 0; i<num_accounts; i++)
+ struct TALER_EXCHANGE_WireAccount was[wr.details.ok.accounts_len];
+
+ wr.details.ok.accounts = was;
+ if (GNUNET_OK !=
+ TALER_EXCHANGE_parse_accounts (&master_pub,
+ accounts,
+ was,
+ wr.details.ok.accounts_len))
{
- struct TALER_EXCHANGE_WireAccount *wa = &was[i];
- json_t *account;
- struct GNUNET_JSON_Specification spec_account[] = {
- GNUNET_JSON_spec_string ("payto_uri",
- &wa->payto_uri),
- GNUNET_JSON_spec_fixed_auto ("master_sig",
- &wa->master_sig),
- GNUNET_JSON_spec_end ()
- };
- char *method;
-
- account = json_array_get (accounts,
- i);
- if (GNUNET_OK !=
- TALER_JSON_exchange_wire_signature_check (account,
- &master_pub))
- {
- /* bogus reply */
- GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_EXCHANGE_WIRE_SIGNATURE_INVALID;
- break;
- }
- if (GNUNET_OK !=
- GNUNET_JSON_parse (account,
- spec_account,
- NULL, NULL))
- {
- /* bogus reply */
- GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- break;
- }
- if (NULL == (method = TALER_payto_get_method (wa->payto_uri)))
- {
- /* bogus reply */
- GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- break;
- }
- if (NULL == (wa->fees = lookup_fee (fm,
- method)))
- {
- /* bogus reply */
- GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- GNUNET_free (method);
- break;
- }
- GNUNET_free (method);
- } /* end 'for all accounts */
- if ( (0 != response_code) &&
- (NULL != wh->cb) )
+ GNUNET_break_op (0);
+ wr.hr.http_status = 0;
+ wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ }
+ else if (NULL != wh->cb)
{
wh->cb (wh->cb_cls,
- &hr,
- num_accounts,
- was);
+ &wr);
wh->cb = NULL;
}
+ TALER_EXCHANGE_free_accounts (was,
+ wr.details.ok.accounts_len);
} /* end of 'parse accounts */
- free_fees (fm);
+ free_fees (fbm,
+ wr.details.ok.fees_len);
GNUNET_JSON_parse_free (spec);
} /* end of MHD_HTTP_OK */
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ wr.hr.ec = TALER_JSON_get_error_code (j);
+ wr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ wr.hr.ec = TALER_JSON_get_error_code (j);
+ wr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ wr.hr.ec = TALER_JSON_get_error_code (j);
+ wr.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
if (MHD_HTTP_GATEWAY_TIMEOUT == response_code)
wh->exchange->wire_error_count++;
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ wr.hr.ec = TALER_JSON_get_error_code (j);
+ wr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange wire\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) wr.hr.ec);
break;
}
if (NULL != wh->cb)
wh->cb (wh->cb_cls,
- &hr,
- 0,
- NULL);
+ &wr);
TALER_EXCHANGE_wire_cancel (wh);
}
diff --git a/src/lib/exchange_api_withdraw.c b/src/lib/exchange_api_withdraw.c
index 62c042039..2a3c095a6 100644
--- a/src/lib/exchange_api_withdraw.c
+++ b/src/lib/exchange_api_withdraw.c
@@ -116,22 +116,20 @@ struct TALER_EXCHANGE_WithdrawHandle
* HTTP /reserves/$RESERVE_PUB/withdraw request.
*
* @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
- * @param hr HTTP response data
- * @param blind_sig blind signature over the coin, NULL on error
+ * @param w2r response data
*/
static void
handle_reserve_withdraw_finished (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_BlindedDenominationSignature *blind_sig)
+ const struct TALER_EXCHANGE_Withdraw2Response *w2r)
{
struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
struct TALER_EXCHANGE_WithdrawResponse wr = {
- .hr = *hr
+ .hr = w2r->hr
};
wh->wh2 = NULL;
- switch (hr->http_status)
+ switch (w2r->hr.http_status)
{
case MHD_HTTP_OK:
{
@@ -139,7 +137,7 @@ handle_reserve_withdraw_finished (
if (GNUNET_OK !=
TALER_planchet_to_coin (&wh->pk.key,
- blind_sig,
+ &w2r->details.ok.blind_sig,
&wh->bks,
&wh->priv,
wh->ach,
@@ -151,10 +149,10 @@ handle_reserve_withdraw_finished (
wr.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE;
break;
}
- wr.details.success.coin_priv = wh->priv;
- wr.details.success.bks = wh->bks;
- wr.details.success.sig = fc.sig;
- wr.details.success.exchange_vals = wh->alg_values;
+ wr.details.ok.coin_priv = wh->priv;
+ wr.details.ok.bks = wh->bks;
+ wr.details.ok.sig = fc.sig;
+ wr.details.ok.exchange_vals = wh->alg_values;
break;
}
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
@@ -170,7 +168,7 @@ handle_reserve_withdraw_finished (
};
if (GNUNET_OK !=
- GNUNET_JSON_parse (hr->reply,
+ GNUNET_JSON_parse (w2r->hr.reply,
spec,
NULL, NULL))
{
@@ -186,8 +184,8 @@ handle_reserve_withdraw_finished (
}
wh->cb (wh->cb_cls,
&wr);
- if (MHD_HTTP_OK == hr->http_status)
- TALER_denom_sig_free (&wr.details.success.sig);
+ if (MHD_HTTP_OK == w2r->hr.http_status)
+ TALER_denom_sig_free (&wr.details.ok.sig);
TALER_EXCHANGE_withdraw_cancel (wh);
}
@@ -213,7 +211,7 @@ withdraw_cs_stage_two_callback (
switch (csrr->hr.http_status)
{
case MHD_HTTP_OK:
- wh->alg_values = csrr->details.success.alg_values;
+ wh->alg_values = csrr->details.ok.alg_values;
TALER_planchet_setup_coin_priv (&wh->ps,
&wh->alg_values,
&wh->priv);
diff --git a/src/lib/exchange_api_withdraw2.c b/src/lib/exchange_api_withdraw2.c
index 04881804a..bf2bd0237 100644
--- a/src/lib/exchange_api_withdraw2.c
+++ b/src/lib/exchange_api_withdraw2.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2022 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
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
@@ -99,16 +99,15 @@ static enum GNUNET_GenericReturnValue
reserve_withdraw_ok (struct TALER_EXCHANGE_Withdraw2Handle *wh,
const json_t *json)
{
- struct TALER_BlindedDenominationSignature blind_sig;
+ struct TALER_EXCHANGE_Withdraw2Response w2r = {
+ .hr.reply = json,
+ .hr.http_status = MHD_HTTP_OK
+ };
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_blinded_denom_sig ("ev_sig",
- &blind_sig),
+ &w2r.details.ok.blind_sig),
GNUNET_JSON_spec_end ()
};
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = json,
- .http_status = MHD_HTTP_OK
- };
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
@@ -121,8 +120,7 @@ reserve_withdraw_ok (struct TALER_EXCHANGE_Withdraw2Handle *wh,
/* signature is valid, return it to the application */
wh->cb (wh->cb_cls,
- &hr,
- &blind_sig);
+ &w2r);
/* make sure callback isn't called again after return */
wh->cb = NULL;
GNUNET_JSON_parse_free (spec);
@@ -240,16 +238,16 @@ handle_reserve_withdraw_finished (void *cls,
{
struct TALER_EXCHANGE_Withdraw2Handle *wh = cls;
const json_t *j = response;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_Withdraw2Response w2r = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
};
wh->job = NULL;
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ w2r.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
@@ -257,8 +255,8 @@ handle_reserve_withdraw_finished (void *cls,
j))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ w2r.hr.http_status = 0;
+ w2r.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
GNUNET_assert (NULL == wh->cb);
@@ -267,24 +265,24 @@ handle_reserve_withdraw_finished (void *cls,
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ w2r.hr.ec = TALER_JSON_get_error_code (j);
+ w2r.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_FORBIDDEN:
GNUNET_break_op (0);
/* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ w2r.hr.ec = TALER_JSON_get_error_code (j);
+ w2r.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, the exchange basically just says
that it doesn't know this reserve. Can happen if we
query before the wire transfer went through.
We should simply pass the JSON reply to the application. */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ w2r.hr.ec = TALER_JSON_get_error_code (j);
+ w2r.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
/* The exchange says that the reserve has insufficient funds;
@@ -294,13 +292,13 @@ handle_reserve_withdraw_finished (void *cls,
j))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ w2r.hr.http_status = 0;
+ w2r.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
}
else
{
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ w2r.hr.ec = TALER_JSON_get_error_code (j);
+ w2r.hr.hint = TALER_JSON_get_error_hint (j);
}
break;
case MHD_HTTP_GONE:
@@ -308,8 +306,8 @@ handle_reserve_withdraw_finished (void *cls,
/* Note: one might want to check /keys for revocation
signature here, alas tricky in case our /keys
is outdated => left to clients */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ w2r.hr.ec = TALER_JSON_get_error_code (j);
+ w2r.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
/* only validate reply is well-formed */
@@ -327,8 +325,8 @@ handle_reserve_withdraw_finished (void *cls,
NULL, NULL))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ w2r.hr.http_status = 0;
+ w2r.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
}
@@ -336,25 +334,24 @@ handle_reserve_withdraw_finished (void *cls,
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ w2r.hr.ec = TALER_JSON_get_error_code (j);
+ w2r.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ w2r.hr.ec = TALER_JSON_get_error_code (j);
+ w2r.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange withdraw\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) w2r.hr.ec);
break;
}
if (NULL != wh->cb)
{
wh->cb (wh->cb_cls,
- &hr,
- NULL);
+ &w2r);
wh->cb = NULL;
}
TALER_EXCHANGE_withdraw2_cancel (wh);
diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c
index ee647f4b7..b1f8417e4 100644
--- a/src/mhd/mhd_parsing.c
+++ b/src/mhd/mhd_parsing.c
@@ -86,25 +86,40 @@ TALER_MHD_parse_post_cleanup_callback (void *con_cls)
}
-enum GNUNET_GenericReturnValue
-TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
- const char *param_name,
- void *out_data,
- size_t out_size)
+/**
+ * Extract fixed-size base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing or
+ * invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the HTTP key with the value
+ * @param kind whether to extract from header, argument or footer
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of @a out_data
+ * @param[out] present set to true if argument was found
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+static enum GNUNET_GenericReturnValue
+parse_request_data (struct MHD_Connection *connection,
+ const char *param_name,
+ enum MHD_ValueKind kind,
+ void *out_data,
+ size_t out_size,
+ bool *present)
{
const char *str;
str = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
+ kind,
param_name);
if (NULL == str)
{
- return (MHD_NO ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MISSING,
- param_name))
- ? GNUNET_SYSERR : GNUNET_NO;
+ *present = false;
+ return GNUNET_OK;
}
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (str,
@@ -117,6 +132,79 @@ TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
param_name))
? GNUNET_SYSERR : GNUNET_NO;
+ *present = true;
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size,
+ bool *present)
+{
+ return parse_request_data (connection,
+ param_name,
+ MHD_GET_ARGUMENT_KIND,
+ out_data,
+ out_size,
+ present);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MHD_parse_request_header_data (struct MHD_Connection *connection,
+ const char *header_name,
+ void *out_data,
+ size_t out_size,
+ bool *present)
+{
+ return parse_request_data (connection,
+ header_name,
+ MHD_HEADER_KIND,
+ out_data,
+ out_size,
+ present);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection,
+ struct GNUNET_TIME_Absolute *expiration)
+{
+ const char *ts;
+ char dummy;
+ unsigned long long tms;
+
+ ts = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "timeout_ms");
+ if (NULL == ts)
+ {
+ *expiration = GNUNET_TIME_UNIT_ZERO_ABS;
+ return GNUNET_OK;
+ }
+ if (1 !=
+ sscanf (ts,
+ "%llu%c",
+ &tms,
+ &dummy))
+ {
+ MHD_RESULT mret;
+
+ GNUNET_break_op (0);
+ mret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "timeout_ms");
+ return (MHD_YES == mret)
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ *expiration = GNUNET_TIME_relative_to_absolute (
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ tms));
return GNUNET_OK;
}
@@ -262,4 +350,59 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection,
}
+enum GNUNET_GenericReturnValue
+TALER_MHD_check_content_length_ (struct MHD_Connection *connection,
+ unsigned long long max_len)
+{
+ const char *cl;
+ unsigned long long cv;
+ char dummy;
+
+ /* Maybe check for maximum upload size
+ and refuse requests if they are just too big. */
+ cl = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONTENT_LENGTH);
+ if (NULL == cl)
+ {
+ return GNUNET_OK;
+#if 0
+ /* wallet currently doesn't always send content-length! */
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MISSING,
+ MHD_HTTP_HEADER_CONTENT_LENGTH))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+#endif
+ }
+ if (1 != sscanf (cl,
+ "%llu%c",
+ &cv,
+ &dummy))
+ {
+ /* Not valid HTTP request, just close connection. */
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ MHD_HTTP_HEADER_CONTENT_LENGTH))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
+ {
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_request_too_large (connection))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
/* end of mhd_parsing.c */
diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c
index 1db608edd..2904b63fb 100644
--- a/src/pq/pq_query_helper.c
+++ b/src/pq/pq_query_helper.c
@@ -202,21 +202,21 @@ qconv_denom_pub (void *cls,
}
len = tlen + sizeof (be);
buf = GNUNET_malloc (len);
- memcpy (buf,
- be,
- sizeof (be));
+ GNUNET_memcpy (buf,
+ be,
+ sizeof (be));
switch (denom_pub->cipher)
{
case TALER_DENOMINATION_RSA:
- memcpy (&buf[sizeof (be)],
- tbuf,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ tbuf,
+ tlen);
GNUNET_free (tbuf);
break;
case TALER_DENOMINATION_CS:
- memcpy (&buf[sizeof (be)],
- &denom_pub->details.cs_public_key,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ &denom_pub->details.cs_public_key,
+ tlen);
break;
default:
GNUNET_assert (0);
@@ -298,21 +298,21 @@ qconv_denom_sig (void *cls,
}
len = tlen + sizeof (be);
buf = GNUNET_malloc (len);
- memcpy (buf,
- &be,
- sizeof (be));
+ GNUNET_memcpy (buf,
+ &be,
+ sizeof (be));
switch (denom_sig->cipher)
{
case TALER_DENOMINATION_RSA:
- memcpy (&buf[sizeof (be)],
- tbuf,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ tbuf,
+ tlen);
GNUNET_free (tbuf);
break;
case TALER_DENOMINATION_CS:
- memcpy (&buf[sizeof (be)],
- &denom_sig->details.cs_signature,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ &denom_sig->details.cs_signature,
+ tlen);
break;
default:
GNUNET_assert (0);
@@ -394,21 +394,21 @@ qconv_blinded_denom_sig (void *cls,
}
len = tlen + sizeof (be);
buf = GNUNET_malloc (len);
- memcpy (buf,
- &be,
- sizeof (be));
+ GNUNET_memcpy (buf,
+ &be,
+ sizeof (be));
switch (denom_sig->cipher)
{
case TALER_DENOMINATION_RSA:
- memcpy (&buf[sizeof (be)],
- tbuf,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ tbuf,
+ tlen);
GNUNET_free (tbuf);
break;
case TALER_DENOMINATION_CS:
- memcpy (&buf[sizeof (be)],
- &denom_sig->details.blinded_cs_answer,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ &denom_sig->details.blinded_cs_answer,
+ tlen);
break;
default:
GNUNET_assert (0);
@@ -487,20 +487,20 @@ qconv_blinded_planchet (void *cls,
}
len = tlen + sizeof (be);
buf = GNUNET_malloc (len);
- memcpy (buf,
- &be,
- sizeof (be));
+ GNUNET_memcpy (buf,
+ &be,
+ sizeof (be));
switch (bp->cipher)
{
case TALER_DENOMINATION_RSA:
- memcpy (&buf[sizeof (be)],
- bp->details.rsa_blinded_planchet.blinded_msg,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ bp->details.rsa_blinded_planchet.blinded_msg,
+ tlen);
break;
case TALER_DENOMINATION_CS:
- memcpy (&buf[sizeof (be)],
- &bp->details.cs_blinded_planchet,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ &bp->details.cs_blinded_planchet,
+ tlen);
break;
default:
GNUNET_assert (0);
@@ -578,17 +578,17 @@ qconv_exchange_withdraw_values (void *cls,
}
len = tlen + sizeof (be);
buf = GNUNET_malloc (len);
- memcpy (buf,
- &be,
- sizeof (be));
+ GNUNET_memcpy (buf,
+ &be,
+ sizeof (be));
switch (alg_values->cipher)
{
case TALER_DENOMINATION_RSA:
break;
case TALER_DENOMINATION_CS:
- memcpy (&buf[sizeof (be)],
- &alg_values->details.cs_values,
- tlen);
+ GNUNET_memcpy (&buf[sizeof (be)],
+ &alg_values->details.cs_values,
+ tlen);
break;
default:
GNUNET_assert (0);
diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c
index 139cf1cbf..9441412d4 100644
--- a/src/pq/pq_result_helper.c
+++ b/src/pq/pq_result_helper.c
@@ -113,9 +113,9 @@ extract_amount_nbo_helper (PGresult *result,
}
len = GNUNET_MIN (TALER_CURRENCY_LEN - 1,
strlen (currency));
- memcpy (r_amount_nbo->currency,
- currency,
- len);
+ GNUNET_memcpy (r_amount_nbo->currency,
+ currency,
+ len);
return GNUNET_OK;
}
@@ -420,9 +420,9 @@ extract_denom_pub (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (be,
- res,
- sizeof (be));
+ GNUNET_memcpy (be,
+ res,
+ sizeof (be));
res += sizeof (be);
len -= sizeof (be);
pk->cipher = ntohl (be[0]);
@@ -445,9 +445,9 @@ extract_denom_pub (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&pk->details.cs_public_key,
- res,
- len);
+ GNUNET_memcpy (&pk->details.cs_public_key,
+ res,
+ len);
return GNUNET_OK;
default:
GNUNET_break (0);
@@ -543,9 +543,9 @@ extract_denom_sig (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&be,
- res,
- sizeof (be));
+ GNUNET_memcpy (&be,
+ res,
+ sizeof (be));
if (0x00 != ntohl (be[1]))
{
GNUNET_break (0);
@@ -572,9 +572,9 @@ extract_denom_sig (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&sig->details.cs_signature,
- res,
- len);
+ GNUNET_memcpy (&sig->details.cs_signature,
+ res,
+ len);
return GNUNET_OK;
default:
GNUNET_break (0);
@@ -670,9 +670,9 @@ extract_blinded_denom_sig (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&be,
- res,
- sizeof (be));
+ GNUNET_memcpy (&be,
+ res,
+ sizeof (be));
if (0x01 != ntohl (be[1])) /* magic marker: blinded */
{
GNUNET_break (0);
@@ -699,9 +699,9 @@ extract_blinded_denom_sig (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&sig->details.blinded_cs_answer,
- res,
- len);
+ GNUNET_memcpy (&sig->details.blinded_cs_answer,
+ res,
+ len);
return GNUNET_OK;
default:
GNUNET_break (0);
@@ -798,9 +798,9 @@ extract_blinded_planchet (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&be,
- res,
- sizeof (be));
+ GNUNET_memcpy (&be,
+ res,
+ sizeof (be));
if (0x0100 != ntohl (be[1])) /* magic marker: blinded */
{
GNUNET_break (0);
@@ -824,9 +824,9 @@ extract_blinded_planchet (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&bp->details.cs_blinded_planchet,
- res,
- len);
+ GNUNET_memcpy (&bp->details.cs_blinded_planchet,
+ res,
+ len);
return GNUNET_OK;
default:
GNUNET_break (0);
@@ -923,9 +923,9 @@ extract_exchange_withdraw_values (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&be,
- res,
- sizeof (be));
+ GNUNET_memcpy (&be,
+ res,
+ sizeof (be));
if (0x010000 != ntohl (be[1])) /* magic marker: EWV */
{
GNUNET_break (0);
@@ -949,9 +949,9 @@ extract_exchange_withdraw_values (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- memcpy (&alg_values->details.cs_values,
- res,
- len);
+ GNUNET_memcpy (&alg_values->details.cs_values,
+ res,
+ len);
return GNUNET_OK;
default:
GNUNET_break (0);
diff --git a/src/templating/.gitignore b/src/templating/.gitignore
index b2bf6ef99..ac9c37f45 100644
--- a/src/templating/.gitignore
+++ b/src/templating/.gitignore
@@ -1 +1,2 @@
test_mustach_jansson
+taler-mustach-tool
diff --git a/src/templating/AUTHORS b/src/templating/AUTHORS
index 2fcc60437..b440c24ed 100644
--- a/src/templating/AUTHORS
+++ b/src/templating/AUTHORS
@@ -4,8 +4,15 @@ Main author:
Contributors:
Abhishek Mishra
Atlas
+ Ben Beasley
+ Gabriel Zachmann
Harold L Marzan
+ Kurt Jung
Lailton Fernando Mariano
+ Lucas Ramage
+ Paramtamtam
+ RekGRpth
+ Ryan Fox
Sami Kerola
Sijmen J. Mulder
Tomasz Sieprawski
@@ -13,6 +20,7 @@ Contributors:
Packagers:
pkgsrc: Sijmen J. Mulder
alpine linux: Lucas Ramage
+ RPM & DEB: Marcus Hardt
Thanks to issue submitters:
Dante Torres
@@ -21,3 +29,4 @@ Thanks to issue submitters:
Mark Bucciarelli
Paul Wisehart
Thierry Fournier
+ SASU OKFT
diff --git a/src/templating/LICENSE-2.0.txt b/src/templating/LICENSE-2.0.txt
deleted file mode 100644
index d64569567..000000000
--- a/src/templating/LICENSE-2.0.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/src/templating/LICENSE.txt b/src/templating/LICENSE.txt
new file mode 100644
index 000000000..495aeefd5
--- /dev/null
+++ b/src/templating/LICENSE.txt
@@ -0,0 +1,14 @@
+
+Copyright (c) 2017-2020 by José Bollo
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
+OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
diff --git a/src/templating/Makefile.am b/src/templating/Makefile.am
index 2501151a8..6b5234dba 100644
--- a/src/templating/Makefile.am
+++ b/src/templating/Makefile.am
@@ -6,6 +6,15 @@ if USE_COVERAGE
XLIB = -lgcov
endif
+noinst_PROGRAMS = \
+ taler-mustach-tool
+
+taler_mustach_tool_SOURCES = \
+ mustach-tool.c \
+ mustach-jansson.h
+taler_mustach_tool_LDADD = \
+ libmustach.la \
+ -ljansson
lib_LTLIBRARIES = \
libtalertemplating.la
@@ -15,6 +24,7 @@ noinst_LTLIBRARIES = \
libtalertemplating_la_SOURCES = \
mustach.c mustach.h \
+ mustach-wrap.c mustach-wrap.h \
mustach-jansson.c mustach-jansson.h \
templating_api.c
libtalertemplating_la_LIBADD = \
@@ -31,6 +41,7 @@ libtalertemplating_la_LDFLAGS = \
libmustach_la_SOURCES = \
mustach.c mustach.h \
+ mustach-wrap.c mustach-wrap.h \
mustach-jansson.c mustach-jansson.h
test_mustach_jansson_SOURCES = \
diff --git a/src/templating/README.md b/src/templating/README.md
index a6df19f64..6699b0e89 100644
--- a/src/templating/README.md
+++ b/src/templating/README.md
@@ -1,16 +1,27 @@
-# Introduction to Mustach 0.99
+# Introduction to Mustach 1.2
`mustach` is a C implementation of the [mustache](http://mustache.github.io "main site for mustache")
template specification.
The main site for `mustach` is on [gitlab](https://gitlab.com/jobol/mustach).
-The best way to use mustach is to copy the files **mustach.h** and **mustach.c**
+The simpliest way to use mustach is to copy the files **mustach.h** and **mustach.c**
directly into your project and use it.
-Alternatively, make and meson files are provided for building `mustach` and
+If you are using one of the JSON libraries listed below, you can get extended feature
+by also including **mustach-wrap.h**, **mustach-wrap.c**, **mustach-XXX.h** and
+**mustach-XXX.c** in your project (see below for **XXX**)
+
+- [json-c](https://github.com/json-c/json-c): use **XXX** = **json-c**
+- [jansson](http://www.digip.org/jansson/): use **XXX** = **jansson**
+- [cJSON](https://github.com/DaveGamble/cJSON): use **XXX** = **cjson**
+
+Alternatively, make and meson files are provided for building `mustach` and
`libmustach.so` shared library.
+Since version 1.0, the makefile allows to compile and install different
+flavours. See below for details.
+
## Distributions offering mustach package
### Alpine Linux
@@ -30,6 +41,13 @@ make
See http://pkgsrc.se/devel/mustach
+## Known projects using Mustach
+
+This [wiki page](https://gitlab.com/jobol/mustach/-/wikis/projects-using-mustach)
+lists the known project that are using mustach and that kindly told it.
+
+Don't hesitate to tell us if you are interested to be listed there.
+
## Using Mustach from sources
The file **mustach.h** is the main documentation. Look at it.
@@ -38,15 +56,25 @@ The current source files are:
- **mustach.c** core implementation of mustache in C
- **mustach.h** header file for core definitions
+- **mustach-wrap.c** generic wrapper of mustach for easier integration
+- **mustach-wrap.h** header file for using mustach-wrap
- **mustach-json-c.c** tiny json wrapper of mustach using [json-c](https://github.com/json-c/json-c)
-- **mustach-json-c.h** header file for using the tiny JSON wrapper
-- **mustach-tool.c** simple tool for applying template files to a JSON file
+- **mustach-json-c.h** header file for using the tiny json-c wrapper
+- **mustach-cjson.c** tiny json wrapper of mustach using [cJSON](https://github.com/DaveGamble/cJSON)
+- **mustach-cjson.h** header file for using the tiny cJSON wrapper
+- **mustach-jansson.c** tiny json wrapper of mustach using [jansson](https://www.digip.org/jansson/)
+- **mustach-jansson.h** header file for using the tiny jansson wrapper
+- **mustach-tool.c** simple tool for applying template files to one JSON file
-The file **mustach-json-c.c** is the main example of use of **mustach** core
-and it is also a practical implementation that can be used. It uses the library
-json-c. (NOTE for Mac OS: available through homebrew).
+The file **mustach-json-c.c** is the historical example of use of **mustach** and
+**mustach-wrap** core and it is also a practical implementation that can be used.
+It uses the library json-c. (NOTE for Mac OS: available through homebrew).
-HELP REQUESTED TO GIVE EXAMPLE BASED ON OTHER LIBRARIES (ex: janson, ...).
+Since version 1.0, the project also provide integration of other JSON libraries:
+**cJSON** and **jansson**.
+
+*If you integrate a new library with* **mustach**, *your contribution will be
+welcome here*.
The tool **mustach** is build using `make`, its usage is:
@@ -57,14 +85,14 @@ It then outputs the result of applying the templates files to the JSON file.
### Portability
Some system does not provide *open_memstream*. In that case, tell your
-preferred compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**.
+prefered compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**.
Example:
- gcc -DNO_OPEN_MEMSTREAM
+ CFLAGS=-DNO_OPEN_MEMSTREAM make
### Integration
-The file **mustach.h** is the main documentation. Look at it.
+The files **mustach.h** and **mustach-wrap.h** are the main documentation. Look at it.
The file **mustach-json-c.c** provides a good example of integration.
@@ -76,139 +104,211 @@ If you intend to use specific escaping and/or specific output, the callbacks
of the interface **mustach_itf** that you have to implement are:
`enter`, `next`, `leave`, `get` and `emit`.
-### Extensions
-
-By default, the current implementation provides the following extensions:
+### Compilation Using Make
-#### Explicit Substitution
+Building and installing can be done using make.
-This is a core extension implemented in file **mustach.c**.
+Example:
-In somecases the name of the key used for substitution begins with a
+ $ make tool=cjson libs=none PREFIX=/usr/local DESTDIR=/ install
+ $ make tool=jsonc libs=single PREFIX=/ DESTDIR=$HOME/.local install
+
+The makefile knows following switches (\*: default):
+
+ Switch name | Values | Description
+ --------------+---------+-----------------------------------------------
+ jsonc | (unset) | Auto detection of json-c
+ | no | Don't compile for json-c
+ | yes | Compile for json-c that must exist
+ --------------+---------+-----------------------------------------------
+ cjson | (unset) | Auto detection of cJSON
+ | no | Don't compile for cJSON
+ | yes | Compile for cJSON that must exist
+ --------------+---------+-----------------------------------------------
+ jansson | (unset) | Auto detection of jansson
+ | no | Don't compile for jansson
+ | yes | Compile for jansson that must exist
+ --------------+---------+-----------------------------------------------
+ tool | (unset) | Auto detection
+ | cjson | Use cjson library
+ | jsonc | Use jsonc library
+ | jansson | Use jansson library
+ | none | Don't compile the tool
+ --------------+---------+----------------------------------------------
+ libs | (unset) | Like 'all'
+ | all | Like 'single' AND 'split'
+ | single | Only libmustach.so
+ | split | All the possible libmustach-XXX.so ...
+ | none | No library is produced
+
+The libraries that can be produced are:
+
+ Library name | Content
+ --------------------+--------------------------------------------------------
+ libmustach-core | mustach.c mustach-wrap.c
+ libmustach-cjson | mustach.c mustach-wrap.c mustach-cjson.c
+ libmustach-jsonc | mustach.c mustach-wrap.c mustach-json-c.c
+ libmustach-jansson | mustach.c mustach-wrap.c mustach-jansson.c
+ libmustach | mustach.c mustach-wrap.c mustach-{cjson,json-c,jansson}.c
+
+There is no dependencies of a library to an other. This is intended and doesn't
+hurt today because the code is small.
+
+## Extensions
+
+The current implementation provides extensions to specifications of **mustache**.
+This extensions can be activated or deactivated using flags.
+
+Here is the summary.
+
+ Flag name | Description
+ -------------------------------+------------------------------------------------
+ Mustach_With_Colon | Explicit tag substition with colon
+ Mustach_With_EmptyTag | Empty Tag Allowed
+ -------------------------------+------------------------------------------------
+ Mustach_With_Equal | Value Testing Equality
+ Mustach_With_Compare | Value Comparing
+ Mustach_With_JsonPointer | Interpret JSON Pointers
+ Mustach_With_ObjectIter | Iteration On Objects
+ Mustach_With_EscFirstCmp | Escape First Compare
+ Mustach_With_ErrorUndefined | Error when a requested tag is undefined
+ -------------------------------+------------------------------------------------
+ Mustach_With_AllExtensions | Activate all known extensions
+ Mustach_With_NoExtensions | Disable any extension
+
+For the details, see below.
+
+### Explicit Tag Substitution With Colon (Mustach_With_Colon)
+
+In somecases the name of the key used for substition begins with a
character reserved for mustach: one of `#`, `^`, `/`, `&`, `{`, `>` and `=`.
+
This extension introduces the special character `:` to explicitly
tell mustach to just substitute the value. So `:` becomes a new special
character.
-#### Value Testing and Comparing
+This is a core extension implemented in file **mustach.c**.
+
+### Empty Tag Allowed (Mustach_With_EmptyTag)
-This are a tool extension implemented in file **mustach-json-c.c**.
+When an empty tag is found, instead of automatically raising the error
+MUSTACH\_ERROR\_EMPTY\_TAG pass it.
-These extensions allows you to test the value of the selected key.
-They allow to write `key=value` (matching test) or `key=!value`
+This is a core extension implemented in file **mustach.c**.
+
+### Value Testing Equality (Mustach_With_Equal)
+
+This extension allows you to test the value of the selected key.
+It allows to write `key=value` (matching test) or `key=!value`
(not matching test) in any query.
-The specific comparison extension also allows to compare if greater,
-lesser, etc.. than a value. It allows to write `key>value`.
+This is a wrap extension implemented in file **mustach-wrap.c**.
+
+### Value Comparing (Mustach_With_Compare)
+
+These extension extends the extension for testing equality to also
+compare values if greater or lesser.
+Its allows to write `key>value` (greater), `key>=value` (greater or equal),
+`key<value` (lesser) and `key<=value` (lesser or equal).
It the comparator sign appears in the first column it is ignored
as if it was escaped.
-#### Access to current value
+This is a wrap extension implemented in file **mustach-wrap.c**.
+
+### Interpret JSON Pointers (Mustach_With_JsonPointer)
-The value of the current field can be accessed using single dot like
-in `{{#key}}{{.}}{{/key}}` that applied to `{"key":3.14}` produces `3.14`
-and `{{#array}} {{.}}{{/array}}` applied to `{"array":[1,2]}` produces
-` 1 2`.
+This extension allows to use JSON pointers as defined in IETF RFC 6901.
+If active, any key starting with "/" is a JSON pointer.
+This implies to use the colon to introduce JSON keys.
-#### Iteration on objects
+A special escaping is used for `=`, `<`, `>` signs when
+values comparisons are enabled: `~=` gives `=` in the key.
-Using the pattern `{{#X.*}}...{{/X.*}}` it is possible to iterate on
-fields of `X`. Example: `{{s.*}} {{*}}:{{.}}{{/s.*}}` applied on
-`{"s":{"a":1,"b":true}}` produces ` a:1 b:true`. Here the single star
-`{{*}}` is replaced by the iterated key and the single dot `{{.}}` is
-replaced by its value.
+This is a wrap extension implemented in file **mustach-wrap.c**.
-### Removing Extensions
+### Iteration On Objects (Mustach_With_ObjectIter)
-When compiling mustach.c or mustach-json-c.c,
-extensions can be removed by defining macros
-using option -D.
+With this extension, using the pattern `{{#X.*}}...{{/X.*}}`
+allows to iterate on fields of `X`.
+
+Example:
-The possible macros are of 3 categories, the global,
-the mustach core specific and the mustach-json-c example
-of implementation specific.
+- `{{s.*}} {{*}}:{{.}}{{/s.*}}` applied on `{"s":{"a":1,"b":true}}` produces ` a:1 b:true`
-#### Global macros
+Here the single star `{{*}}` is replaced by the iterated key
+and the single dot `{{.}}` is replaced by its value.
-- `NO_EXTENSION_FOR_MUSTACH`
+This is a wrap extension implemented in file **mustach-wrap.c**.
- This macro disables any current or future
- extensions for the core or the example.
+### Error when a requested tag is undefined (Mustach_With_ErrorUndefined)
-#### Macros for the core mustach engine (mustach.c)
+Report the error MUSTACH_ERROR_UNDEFINED_TAG when a requested tag
+is not defined.
-- `NO_COLON_EXTENSION_FOR_MUSTACH`
+This is a wrap extension implemented in file **mustach-wrap.c**.
- This macro remove the ability to use colon (:)
- as explicit command for variable substitution.
- This extension allows to have name starting
- with one of the mustach character `:#^/&{=>`
+### Access To Current Value
-- `NO_ALLOW_EMPTY_TAG`
+*this was an extension but is now always enforced*
- Generate the error MUSTACH_ERROR_EMPTY_TAG automatically
- when an empty tag is encountered.
+The value of the current field can be accessed using single dot.
-#### Macros for the implementation example (mustach-json-c.c)
+Examples:
-- `NO_EQUAL_VALUE_EXTENSION_FOR_MUSTACH`
+- `{{#key}}{{.}}{{/key}}` applied to `{"key":3.14}` produces `3.14`
+- `{{#array}} {{.}}{{/array}}` applied to `{"array":[1,2]}` produces ` 1 2`.
- This macro allows the program to check whether
- the actual value is equal to an expected value.
- This is useful in `{{#key=val}}` or `{{^key=val}}`
- with the corresponding `{{/key=val}}`.
- It can also be used in `{{key=val}}` but this
- doesn't seem to be useful.
+This is a wrap extension implemented in file **mustach-wrap.c**.
-- `NO_COMPARE_VALUE_EXTENSION_FOR_MUSTACH`
+### Partial Data First
- This macro allows the program to compare the actual
- value with an expected value. The comparison operators
- are `=`, `>`, `<`, `>=`, `<=`. The meaning of the
- comparison numeric or alphabetic depends on the type
- of the inspected value. Also the result of the comparison
- can be inverted if the value starts with `!`.
- Example of use: `{{key>=val}}`, or `{{#key>=val}}` and
- `{{^key>=val}}` with their matching `{{/key>=val}}`.
+*this was an extension but is now always enforced*
-- `NO_USE_VALUE_ESCAPE_FIRST_EXTENSION_FOR_MUSTACH`
+The default resolution for partial pattern like `{{> name}}`
+is to search for `name` in the current json context and
+as a file named `name` or if not found `name.mustache`.
- This macro fordids automatic escaping of coparison
- sign appearing at first column.
+By default, the order of the search is (1) as a file,
+and if not found, (2) in the current json context.
-- `NO_JSON_POINTER_EXTENSION_FOR_MUSTACH`
+When this option is set, the order is reverted and content
+of partial is search (1) in the current json context,
+and if not found, (2) as a file.
- This macro removes the possible use of JSON pointers.
- JSON pointers are defined in IETF RFC 6901.
- If not set, any key starting with "/" is a JSON pointer.
- This implies to use the colon to introduce keys.
- So `NO_COLON_EXTENSION_FOR_MUSTACH` implies
- `NO_JSON_POINTER_EXTENSION_FOR_MUSTACH`.
- A special escaping is used for `=`, `<`, `>` signs when
- values comparisons are enabled: `~=` gives `=` in the key.
+That option is useful to keep the compatibility with
+versions of *mustach* anteriors to 1.2.0.
+
+This is a wrap extension implemented in file **mustach-wrap.c**.
+
+### Escape First Compare
+
+This extension automatically escapes comparisons appears as
+first characters.
+
+This is a wrap extension implemented in file **mustach-wrap.c**.
+
+## Difference with version 0.99 and previous
+
+### Extensions
-- `NO_OBJECT_ITERATION_FOR_MUSTACH`
+The extensions can no more be removed at compile time, use
+flags to select your required extension on need.
- Disable the object iteration extension. That extension allows
- to iterate over the keys of an object. The iteration on object
- is selected by using the selector `{{#key.*}}`. In the context
- of iterating over object keys, the single key `{{*}}` returns the
- key and `{{.}}` returns the value.
+### Name of functions
-- `NO_SINGLE_DOT_EXTENSION_FOR_MUSTACH`
+Names of functions were improved. Old names remain but are obsolete
+and legacy. Their removal in far future versions is possible.
- Disable access to current object value using single dot
- like in `{{.}}`.
+The table below summarize the changes.
-- `NO_INCLUDE_PARTIAL_FALLBACK`
+ legacy name | name since version 1.0.0
+ ------------------+-----------------------
+ fmustach | mustach_file
+ fdmustach | mustach_fd
+ mustach | mustach_mem
+ fmustach_json_c | mustach_json_c_file
+ fdmustach_json_c | mustach_json_c_fd
+ mustach_json_c | mustach_json_c_mem
+ mustach_json_c | mustach_json_c_write
- Disable include of file by partial pattern like `{{> name}}`.
- By default if a such pattern is found, **mustach** search
- for `name` in the current json context. This what is done
- historically and when `NO_INCLUDE_PARTIAL_FALLBACK` is defined.
- When `NO_INCLUDE_PARTIAL_FALLBACK` is defined, if the value is
- found in the json context, the files `name` and `name.mustache`
- are searched in that order and the first file found is used
- as partial content. The macro `INCLUDE_PARTIAL_EXTENSION` can
- be use for changing the extension added.
diff --git a/src/templating/mustach-cjson.c b/src/templating/mustach-cjson.c
new file mode 100644
index 000000000..48b97e7df
--- /dev/null
+++ b/src/templating/mustach-cjson.c
@@ -0,0 +1,239 @@
+/*
+ Author: José Bollo <jobol@nonadev.net>
+
+ https://gitlab.com/jobol/mustach
+
+ SPDX-License-Identifier: ISC
+*/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "mustach.h"
+#include "mustach-wrap.h"
+#include "mustach-cjson.h"
+
+struct expl {
+ cJSON null;
+ cJSON *root;
+ cJSON *selection;
+ int depth;
+ struct {
+ cJSON *cont;
+ cJSON *obj;
+ cJSON *next;
+ int is_objiter;
+ } stack[MUSTACH_MAX_DEPTH];
+};
+
+static int start(void *closure)
+{
+ struct expl *e = closure;
+ e->depth = 0;
+ memset(&e->null, 0, sizeof e->null);
+ e->null.type = cJSON_NULL;
+ e->selection = &e->null;
+ e->stack[0].cont = NULL;
+ e->stack[0].obj = e->root;
+ return MUSTACH_OK;
+}
+
+static int compare(void *closure, const char *value)
+{
+ struct expl *e = closure;
+ cJSON *o = e->selection;
+ double d;
+
+ if (cJSON_IsNumber(o)) {
+ d = o->valuedouble - atof(value);
+ return d < 0 ? -1 : d > 0 ? 1 : 0;
+ } else if (cJSON_IsString(o)) {
+ return strcmp(o->valuestring, value);
+ } else if (cJSON_IsTrue(o)) {
+ return strcmp("true", value);
+ } else if (cJSON_IsFalse(o)) {
+ return strcmp("false", value);
+ } else if (cJSON_IsNull(o)) {
+ return strcmp("null", value);
+ } else {
+ return 1;
+ }
+}
+
+static int sel(void *closure, const char *name)
+{
+ struct expl *e = closure;
+ cJSON *o;
+ int i, r;
+
+ if (name == NULL) {
+ o = e->stack[e->depth].obj;
+ r = 1;
+ } else {
+ i = e->depth;
+ while (i >= 0 && !(o = cJSON_GetObjectItemCaseSensitive(e->stack[i].obj, name)))
+ i--;
+ if (i >= 0)
+ r = 1;
+ else {
+ o = &e->null;
+ r = 0;
+ }
+ }
+ e->selection = o;
+ return r;
+}
+
+static int subsel(void *closure, const char *name)
+{
+ struct expl *e = closure;
+ cJSON *o;
+ int r;
+
+ o = cJSON_GetObjectItemCaseSensitive(e->selection, name);
+ r = o != NULL;
+ if (r)
+ e->selection = o;
+ return r;
+}
+
+static int enter(void *closure, int objiter)
+{
+ struct expl *e = closure;
+ cJSON *o;
+
+ if (++e->depth >= MUSTACH_MAX_DEPTH)
+ return MUSTACH_ERROR_TOO_DEEP;
+
+ o = e->selection;
+ e->stack[e->depth].is_objiter = 0;
+ if (objiter) {
+ if (! cJSON_IsObject(o))
+ goto not_entering;
+ if (o->child == NULL)
+ goto not_entering;
+ e->stack[e->depth].obj = o->child;
+ e->stack[e->depth].next = o->child->next;
+ e->stack[e->depth].cont = o;
+ e->stack[e->depth].is_objiter = 1;
+ } else if (cJSON_IsArray(o)) {
+ if (o->child == NULL)
+ goto not_entering;
+ e->stack[e->depth].obj = o->child;
+ e->stack[e->depth].next = o->child->next;
+ e->stack[e->depth].cont = o;
+ } else if ((cJSON_IsObject(o) && o->child == NULL) || (! cJSON_IsFalse(o) && ! cJSON_IsNull(o))) {
+ e->stack[e->depth].obj = o;
+ e->stack[e->depth].cont = NULL;
+ e->stack[e->depth].next = NULL;
+ } else
+ goto not_entering;
+ return 1;
+
+not_entering:
+ e->depth--;
+ return 0;
+}
+
+static int next(void *closure)
+{
+ struct expl *e = closure;
+ cJSON *o;
+
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
+
+ o = e->stack[e->depth].next;
+ if (o == NULL)
+ return 0;
+
+ e->stack[e->depth].obj = o;
+ e->stack[e->depth].next = o->next;
+ return 1;
+}
+
+static int leave(void *closure)
+{
+ struct expl *e = closure;
+
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
+
+ e->depth--;
+ return 0;
+}
+
+static int get(void *closure, struct mustach_sbuf *sbuf, int key)
+{
+ struct expl *e = closure;
+ const char *s;
+
+ if (key) {
+ s = e->stack[e->depth].is_objiter
+ ? e->stack[e->depth].obj->string
+ : "";
+ }
+ else if (cJSON_IsString(e->selection))
+ s = e->selection->valuestring;
+ else if (cJSON_IsNull(e->selection))
+ s = "";
+ else {
+ s = cJSON_PrintUnformatted(e->selection);
+ if (s == NULL)
+ return MUSTACH_ERROR_SYSTEM;
+ sbuf->freecb = cJSON_free;
+ }
+ sbuf->value = s;
+ return 1;
+}
+
+const struct mustach_wrap_itf mustach_cJSON_wrap_itf = {
+ .start = start,
+ .stop = NULL,
+ .compare = compare,
+ .sel = sel,
+ .subsel = subsel,
+ .enter = enter,
+ .next = next,
+ .leave = leave,
+ .get = get
+};
+
+int mustach_cJSON_file(const char *template, size_t length, cJSON *root, int flags, FILE *file)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_file(template, length, &mustach_cJSON_wrap_itf, &e, flags, file);
+}
+
+int mustach_cJSON_fd(const char *template, size_t length, cJSON *root, int flags, int fd)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_fd(template, length, &mustach_cJSON_wrap_itf, &e, flags, fd);
+}
+
+int mustach_cJSON_mem(const char *template, size_t length, cJSON *root, int flags, char **result, size_t *size)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_mem(template, length, &mustach_cJSON_wrap_itf, &e, flags, result, size);
+}
+
+int mustach_cJSON_write(const char *template, size_t length, cJSON *root, int flags, mustach_write_cb_t *writecb, void *closure)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_write(template, length, &mustach_cJSON_wrap_itf, &e, flags, writecb, closure);
+}
+
+int mustach_cJSON_emit(const char *template, size_t length, cJSON *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_emit(template, length, &mustach_cJSON_wrap_itf, &e, flags, emitcb, closure);
+}
+
diff --git a/src/templating/mustach-cjson.h b/src/templating/mustach-cjson.h
new file mode 100644
index 000000000..ae0d818cb
--- /dev/null
+++ b/src/templating/mustach-cjson.h
@@ -0,0 +1,96 @@
+/*
+ Author: José Bollo <jobol@nonadev.net>
+
+ https://gitlab.com/jobol/mustach
+
+ SPDX-License-Identifier: ISC
+*/
+
+#ifndef _mustach_cJSON_h_included_
+#define _mustach_cJSON_h_included_
+
+/*
+ * mustach-json-c is intended to make integration of cJSON
+ * library by providing integrated functions.
+ */
+
+#include <cjson/cJSON.h>
+#include "mustach-wrap.h"
+
+/**
+ * Wrap interface used internally by mustach cJSON functions.
+ * Can be used for overriding behaviour.
+ */
+extern const struct mustach_wrap_itf mustach_cJSON_wrap_itf;
+
+/**
+ * mustach_cJSON_file - Renders the mustache 'template' in 'file' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @file: the file where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_cJSON_file(const char *template, size_t length, cJSON *root, int flags, FILE *file);
+
+/**
+ * mustach_cJSON_fd - Renders the mustache 'template' in 'fd' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @fd: the file descriptor number where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_cJSON_fd(const char *template, size_t length, cJSON *root, int flags, int fd);
+
+
+/**
+ * mustach_cJSON_mem - Renders the mustache 'template' in 'result' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @result: the pointer receiving the result when 0 is returned
+ * @size: the size of the returned result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_cJSON_mem(const char *template, size_t length, cJSON *root, int flags, char **result, size_t *size);
+
+/**
+ * mustach_cJSON_write - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @writecb: the function that write values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_cJSON_write(const char *template, size_t length, cJSON *root, int flags, mustach_write_cb_t *writecb, void *closure);
+
+/**
+ * mustach_cJSON_emit - Renders the mustache 'template' for 'root' to custom emiter 'emitcb' with 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @emitcb: the function that emit values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_cJSON_emit(const char *template, size_t length, cJSON *root, int flags, mustach_emit_cb_t *emitcb, void *closure);
+
+#endif
+
diff --git a/src/templating/mustach-jansson.c b/src/templating/mustach-jansson.c
index c65fe2b01..3ce0e0a84 100644
--- a/src/templating/mustach-jansson.c
+++ b/src/templating/mustach-jansson.c
@@ -1,429 +1,251 @@
/*
- Copyright (C) 2020 Taler Systems SA
-
- Original license:
Author: José Bollo <jobol@nonadev.net>
- Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+ SPDX-License-Identifier: ISC
+*/
- http://www.apache.org/licenses/LICENSE-2.0
+#define _GNU_SOURCE
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
+#include <stdio.h>
+#include <string.h>
-#include "platform.h"
+#include "mustach.h"
+#include "mustach-wrap.h"
#include "mustach-jansson.h"
-struct Context
-{
- /**
- * Context object.
- */
- json_t *cont;
-
- /**
- * Current object.
- */
- json_t *obj;
-
- /**
- * Opaque object iterator.
- */
- void *iter;
-
- /**
- * Current index when iterating over an array.
- */
- unsigned int index;
-
- /**
- * Count when iterating over an array.
- */
- unsigned int count;
-
- bool is_objiter;
+struct expl {
+ json_t *root;
+ json_t *selection;
+ int depth;
+ struct {
+ json_t *cont;
+ json_t *obj;
+ void *iter;
+ int is_objiter;
+ size_t index, count;
+ } stack[MUSTACH_MAX_DEPTH];
};
-enum Bang
+static int start(void *closure)
{
- BANG_NONE,
- BANG_I18N,
- BANG_STRINGIFY,
- BANG_AMOUNT_CURRENCY,
- BANG_AMOUNT_DECIMAL,
-};
-
-struct JanssonClosure
-{
- json_t *root;
- mustach_jansson_write_cb writecb;
- int depth;
-
- /**
- * Did the last find(..) call result in an iterable?
- */
- struct Context stack[MUSTACH_MAX_DEPTH];
-
- /**
- * The last object we found should be iterated over.
- */
- bool found_iter;
-
- /**
- * Last bang we found.
- */
- enum Bang found_bang;
-
- /**
- * Language for i18n lookups.
- */
- const char *lang;
-};
-
-
-static json_t *
-walk (json_t *obj, const char *path)
-{
- char *saveptr = NULL;
- char *sp = GNUNET_strdup (path);
- char *p = sp;
- while (true)
- {
- char *tok = strtok_r (p, ".", &saveptr);
- if (tok == NULL)
- break;
- obj = json_object_get (obj, tok);
- if (obj == NULL)
- break;
- p = NULL;
- }
- GNUNET_free (sp);
- return obj;
+ struct expl *e = closure;
+ e->depth = 0;
+ e->selection = json_null();
+ e->stack[0].cont = NULL;
+ e->stack[0].obj = e->root;
+ e->stack[0].index = 0;
+ e->stack[0].count = 1;
+ return MUSTACH_OK;
}
-
-static json_t *
-find (struct JanssonClosure *e, const char *name)
-{
- json_t *obj = NULL;
- char *path = GNUNET_strdup (name);
- char *bang;
-
- bang = strchr (path, '!');
-
- e->found_bang = BANG_NONE;
-
- if (NULL != bang)
- {
- *bang = 0;
- bang++;
-
- if (0 == strcmp (bang, "i18n"))
- e->found_bang = BANG_I18N;
- else if (0 == strcmp (bang, "stringify"))
- e->found_bang = BANG_STRINGIFY;
- else if (0 == strcmp (bang, "amount_decimal"))
- e->found_bang = BANG_AMOUNT_CURRENCY;
- else if (0 == strcmp (bang, "amount_currency"))
- e->found_bang = BANG_AMOUNT_DECIMAL;
- }
-
- if (BANG_I18N == e->found_bang && NULL != e->lang)
- {
- char *aug_path;
- GNUNET_asprintf (&aug_path, "%s_i18n.%s", path, e->lang);
- obj = walk (e->stack[e->depth].obj, aug_path);
- GNUNET_free (aug_path);
- }
-
- if (NULL == obj)
- {
- obj = walk (e->stack[e->depth].obj, path);
- }
-
- GNUNET_free (path);
-
- return obj;
+static int compare(void *closure, const char *value)
+{
+ struct expl *e = closure;
+ json_t *o = e->selection;
+ double d;
+ json_int_t i;
+
+ switch (json_typeof(o)) {
+ case JSON_REAL:
+ d = json_number_value(o) - atof(value);
+ return d < 0 ? -1 : d > 0 ? 1 : 0;
+ case JSON_INTEGER:
+ i = (json_int_t)json_integer_value(o) - (json_int_t)atoll(value);
+ return i < 0 ? -1 : i > 0 ? 1 : 0;
+ case JSON_STRING:
+ return strcmp(json_string_value(o), value);
+ case JSON_TRUE:
+ return strcmp("true", value);
+ case JSON_FALSE:
+ return strcmp("false", value);
+ case JSON_NULL:
+ return strcmp("null", value);
+ default:
+ return 1;
+ }
}
-
-static int
-start (void *closure)
-{
- struct JanssonClosure *e = closure;
- e->depth = 0;
- e->stack[0].cont = NULL;
- e->stack[0].obj = e->root;
- e->stack[0].index = 0;
- e->stack[0].count = 1;
- e->lang = json_string_value (json_object_get (e->root, "$language"));
- return MUSTACH_OK;
+static int sel(void *closure, const char *name)
+{
+ struct expl *e = closure;
+ json_t *o;
+ int i, r;
+
+ if (name == NULL) {
+ o = e->stack[e->depth].obj;
+ r = 1;
+ } else {
+ i = e->depth;
+ while (i >= 0 && !(o = json_object_get(e->stack[i].obj, name)))
+ i--;
+ if (i >= 0)
+ r = 1;
+ else {
+ o = json_null();
+ r = 0;
+ }
+ }
+ e->selection = o;
+ return r;
}
-
-static int
-emituw (void *closure, const char *buffer, size_t size, int escape, FILE *file)
+static int subsel(void *closure, const char *name)
{
- struct JanssonClosure *e = closure;
- if (! escape)
- e->writecb (file, buffer, size);
- else
- do
- {
- switch (*buffer)
- {
- case '<':
- e->writecb (file, "&lt;", 4);
- break;
- case '>':
- e->writecb (file, "&gt;", 4);
- break;
- case '&':
- e->writecb (file, "&amp;", 5);
- break;
- default:
- e->writecb (file, buffer, 1);
- break;
- }
- buffer++;
- }
- while(--size);
- return MUSTACH_OK;
+ struct expl *e = closure;
+ json_t *o;
+ int r;
+
+ o = json_object_get(e->selection, name);
+ r = o != NULL;
+ if (r)
+ e->selection = o;
+ return r;
}
+static int enter(void *closure, int objiter)
+{
+ struct expl *e = closure;
+ json_t *o;
+
+ if (++e->depth >= MUSTACH_MAX_DEPTH)
+ return MUSTACH_ERROR_TOO_DEEP;
+
+ o = e->selection;
+ e->stack[e->depth].is_objiter = 0;
+ if (objiter) {
+ if (!json_is_object(o))
+ goto not_entering;
+ e->stack[e->depth].iter = json_object_iter(o);
+ if (e->stack[e->depth].iter == NULL)
+ goto not_entering;
+ e->stack[e->depth].obj = json_object_iter_value(e->stack[e->depth].iter);
+ e->stack[e->depth].cont = o;
+ e->stack[e->depth].is_objiter = 1;
+ } else if (json_is_array(o)) {
+ e->stack[e->depth].count = json_array_size(o);
+ if (e->stack[e->depth].count == 0)
+ goto not_entering;
+ e->stack[e->depth].cont = o;
+ e->stack[e->depth].obj = json_array_get(o, 0);
+ e->stack[e->depth].index = 0;
+ } else if ((json_is_object(o) && json_object_size(0)) || (!json_is_false(o) && !json_is_null(o))) {
+ e->stack[e->depth].count = 1;
+ e->stack[e->depth].cont = NULL;
+ e->stack[e->depth].obj = o;
+ e->stack[e->depth].index = 0;
+ } else
+ goto not_entering;
+ return 1;
+
+not_entering:
+ e->depth--;
+ return 0;
+}
-static int
-enter (void *closure, const char *name)
+static int next(void *closure)
{
- struct JanssonClosure *e = closure;
- json_t *o = find (e, name);
- if (++e->depth >= MUSTACH_MAX_DEPTH)
- return MUSTACH_ERROR_TOO_DEEP;
-
- if (json_is_object (o))
- {
- if (e->found_iter)
- {
- void *iter = json_object_iter (o);
- if (NULL == iter)
- {
- e->depth--;
- return 0;
- }
- e->stack[e->depth].is_objiter = 1;
- e->stack[e->depth].iter = iter;
- e->stack[e->depth].obj = json_object_iter_value (iter);
- e->stack[e->depth].cont = o;
- }
- else
- {
- e->stack[e->depth].is_objiter = 0;
- e->stack[e->depth].obj = o;
- e->stack[e->depth].cont = o;
- }
- return 1;
- }
-
- if (json_is_array (o))
- {
- unsigned int size = json_array_size (o);
- if (size == 0)
- {
- e->depth--;
- return 0;
- }
- e->stack[e->depth].count = size;
- e->stack[e->depth].cont = o;
- e->stack[e->depth].obj = json_array_get (o, 0);
- e->stack[e->depth].index = 0;
- e->stack[e->depth].is_objiter = 0;
- return 1;
- }
-
- e->depth--;
- return 0;
-}
+ struct expl *e = closure;
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
-static int
-next (void *closure)
-{
- struct JanssonClosure *e = closure;
- struct Context *ctx;
- if (e->depth <= 0)
- return MUSTACH_ERROR_CLOSING;
- ctx = &e->stack[e->depth];
- if (ctx->is_objiter)
- {
- ctx->iter = json_object_iter_next (ctx->obj, ctx->iter);
- if (NULL == ctx->iter)
- return 0;
- ctx->obj = json_object_iter_value (ctx->iter);
- return 1;
- }
- ctx->index++;
- if (ctx->index >= ctx->count)
- return 0;
- ctx->obj = json_array_get (ctx->cont, ctx->index);
- return 1;
-}
+ if (e->stack[e->depth].is_objiter) {
+ e->stack[e->depth].iter = json_object_iter_next(e->stack[e->depth].cont, e->stack[e->depth].iter);
+ if (e->stack[e->depth].iter == NULL)
+ return 0;
+ e->stack[e->depth].obj = json_object_iter_value(e->stack[e->depth].iter);
+ return 1;
+ }
+ e->stack[e->depth].index++;
+ if (e->stack[e->depth].index >= e->stack[e->depth].count)
+ return 0;
-static int
-leave (void *closure)
-{
- struct JanssonClosure *e = closure;
- if (e->depth <= 0)
- return MUSTACH_ERROR_CLOSING;
- e->depth--;
- return 0;
+ e->stack[e->depth].obj = json_array_get(e->stack[e->depth].cont, e->stack[e->depth].index);
+ return 1;
}
-
-static void
-freecb (void *v)
+static int leave(void *closure)
{
- free (v);
-}
+ struct expl *e = closure;
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
-static int
-get (void *closure, const char *name, struct mustach_sbuf *sbuf)
-{
- struct JanssonClosure *e = closure;
- json_t *obj;
-
- if ( (0 == strcmp (name, "*") ) &&
- (e->stack[e->depth].is_objiter) )
- {
- sbuf->value = json_object_iter_key (e->stack[e->depth].iter);
- return MUSTACH_OK;
- }
- obj = find (e, name);
- if (NULL != obj)
- {
- switch (e->found_bang)
- {
- case BANG_I18N:
- case BANG_NONE:
- {
- const char *s = json_string_value (obj);
- if (NULL != s)
- {
- sbuf->value = s;
- return MUSTACH_OK;
- }
- }
- break;
- case BANG_STRINGIFY:
- sbuf->value = json_dumps (obj, JSON_INDENT (2));
- sbuf->freecb = freecb;
- return MUSTACH_OK;
- case BANG_AMOUNT_DECIMAL:
- {
- char *s;
- char *c;
- if (! json_is_string (obj))
- break;
- s = GNUNET_strdup (json_string_value (obj));
- c = strchr (s, ':');
- if (NULL != c)
- *c = 0;
- sbuf->value = s;
- sbuf->freecb = freecb;
- return MUSTACH_OK;
- }
- break;
- case BANG_AMOUNT_CURRENCY:
- {
- const char *s;
- if (! json_is_string (obj))
- break;
- s = json_string_value (obj);
- s = strchr (s, ':');
- if (NULL == s)
- break;
- sbuf->value = s + 1;
- return MUSTACH_OK;
- }
- break;
- default:
- break;
- }
- }
- sbuf->value = "";
- return MUSTACH_OK;
+ e->depth--;
+ return 0;
}
+static int get(void *closure, struct mustach_sbuf *sbuf, int key)
+{
+ struct expl *e = closure;
+ const char *s;
+
+ if (key) {
+ s = e->stack[e->depth].is_objiter
+ ? json_object_iter_key(e->stack[e->depth].iter)
+ : "";
+ }
+ else if (json_is_string(e->selection))
+ s = json_string_value(e->selection);
+ else if (json_is_null(e->selection))
+ s = "";
+ else {
+ s = json_dumps(e->selection, JSON_ENCODE_ANY | JSON_COMPACT);
+ if (s == NULL)
+ return MUSTACH_ERROR_SYSTEM;
+ sbuf->freecb = free;
+ }
+ sbuf->value = s;
+ return 1;
+}
-static struct mustach_itf itf = {
- .start = start,
- .put = NULL,
- .enter = enter,
- .next = next,
- .leave = leave,
- .partial = NULL,
- .get = get,
- .emit = NULL,
- .stop = NULL
-};
-
-static struct mustach_itf itfuw = {
- .start = start,
- .put = NULL,
- .enter = enter,
- .next = next,
- .leave = leave,
- .partial = NULL,
- .get = get,
- .emit = emituw,
- .stop = NULL
+const struct mustach_wrap_itf mustach_jansson_wrap_itf = {
+ .start = start,
+ .stop = NULL,
+ .compare = compare,
+ .sel = sel,
+ .subsel = subsel,
+ .enter = enter,
+ .next = next,
+ .leave = leave,
+ .get = get
};
-int
-fmustach_jansson (const char *template, json_t *root, FILE *file)
+int mustach_jansson_file(const char *template, size_t length, json_t *root, int flags, FILE *file)
{
- struct JanssonClosure e = { 0 };
- e.root = root;
- return fmustach (template, &itf, &e, file);
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_file(template, length, &mustach_jansson_wrap_itf, &e, flags, file);
}
-
-int
-fdmustach_jansson (const char *template, json_t *root, int fd)
+int mustach_jansson_fd(const char *template, size_t length, json_t *root, int flags, int fd)
{
- struct JanssonClosure e = { 0 };
- e.root = root;
- return fdmustach (template, &itf, &e, fd);
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_fd(template, length, &mustach_jansson_wrap_itf, &e, flags, fd);
}
-
-int
-mustach_jansson (const char *template, json_t *root, char **result,
- size_t *size)
+int mustach_jansson_mem(const char *template, size_t length, json_t *root, int flags, char **result, size_t *size)
{
- struct JanssonClosure e = { 0 };
- e.root = root;
- e.writecb = NULL;
- return mustach (template, &itf, &e, result, size);
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_mem(template, length, &mustach_jansson_wrap_itf, &e, flags, result, size);
}
+int mustach_jansson_write(const char *template, size_t length, json_t *root, int flags, mustach_write_cb_t *writecb, void *closure)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_write(template, length, &mustach_jansson_wrap_itf, &e, flags, writecb, closure);
+}
-int
-umustach_jansson (const char *template, json_t *root, mustach_jansson_write_cb
- writecb, void *closure)
+int mustach_jansson_emit(const char *template, size_t length, json_t *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
{
- struct JanssonClosure e = { 0 };
- e.root = root;
- e.writecb = writecb;
- return fmustach (template, &itfuw, &e, closure);
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_emit(template, length, &mustach_jansson_wrap_itf, &e, flags, emitcb, closure);
}
+
diff --git a/src/templating/mustach-jansson.h b/src/templating/mustach-jansson.h
index 8fe989fa5..8def948e0 100644
--- a/src/templating/mustach-jansson.h
+++ b/src/templating/mustach-jansson.h
@@ -1,60 +1,60 @@
/*
- Copyright (C) 2020 Taler Systems SA
-
- Original license:
- Author: José Bollo <jose.bollo@iot.bzh>
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ SPDX-License-Identifier: ISC
*/
#ifndef _mustach_jansson_h_included_
#define _mustach_jansson_h_included_
-#include "taler_json_lib.h"
-#include "mustach.h"
+/*
+ * mustach-jansson is intended to make integration of jansson
+ * library by providing integrated functions.
+ */
+
+#include <jansson.h>
+#include "mustach-wrap.h"
/**
- * fmustach_jansson - Renders the mustache 'template' in 'file' for 'root'.
+ * Wrap interface used internally by mustach jansson functions.
+ * Can be used for overriding behaviour.
+ */
+extern const struct mustach_wrap_itf mustach_jansson_wrap_itf;
+
+/**
+ * mustach_jansson_file - Renders the mustache 'template' in 'file' for 'root'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
- * \@file: the file where to write the result
+ * @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int fmustach_jansson (const char *template, json_t *root, FILE *file);
+extern int mustach_jansson_file(const char *template, size_t length, json_t *root, int flags, FILE *file);
/**
- * fmustach_jansson - Renders the mustache 'template' in 'fd' for 'root'.
+ * mustach_jansson_fd - Renders the mustache 'template' in 'fd' for 'root'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int fdmustach_jansson (const char *template, json_t *root, int fd);
+extern int mustach_jansson_fd(const char *template, size_t length, json_t *root, int flags, int fd);
/**
- * fmustach_jansson - Renders the mustache 'template' in 'result' for 'root'.
+ * mustach_jansson_mem - Renders the mustache 'template' in 'result' for 'root'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
@@ -62,13 +62,13 @@ extern int fdmustach_jansson (const char *template, json_t *root, int fd);
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int mustach_jansson (const char *template, json_t *root, char **result,
- size_t *size);
+extern int mustach_jansson_mem(const char *template, size_t length, json_t *root, int flags, char **result, size_t *size);
/**
- * umustach_jansson - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
+ * mustach_jansson_write - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @writecb: the function that write values
* @closure: the closure for the write function
@@ -76,9 +76,21 @@ extern int mustach_jansson (const char *template, json_t *root, char **result,
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-typedef int (*mustach_jansson_write_cb)(void *closure, const char *buffer,
- size_t size);
-extern int umustach_jansson (const char *template, json_t *root,
- mustach_jansson_write_cb writecb, void *closure);
+extern int mustach_jansson_write(const char *template, size_t length, json_t *root, int flags, mustach_write_cb_t *writecb, void *closure);
+
+/**
+ * mustach_jansson_emit - Renders the mustache 'template' for 'root' to custom emiter 'emitcb' with 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @emitcb: the function that emit values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_jansson_emit(const char *template, size_t length, json_t *root, int flags, mustach_emit_cb_t *emitcb, void *closure);
#endif
+
diff --git a/src/templating/mustach-json-c.c b/src/templating/mustach-json-c.c
new file mode 100644
index 000000000..a21a113fb
--- /dev/null
+++ b/src/templating/mustach-json-c.c
@@ -0,0 +1,267 @@
+/*
+ Author: José Bollo <jobol@nonadev.net>
+
+ https://gitlab.com/jobol/mustach
+
+ SPDX-License-Identifier: ISC
+*/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mustach.h"
+#include "mustach-wrap.h"
+#include "mustach-json-c.h"
+
+struct expl {
+ struct json_object *root;
+ struct json_object *selection;
+ int depth;
+ struct {
+ struct json_object *cont;
+ struct json_object *obj;
+ struct json_object_iterator iter;
+ struct json_object_iterator enditer;
+ int is_objiter;
+ int index, count;
+ } stack[MUSTACH_MAX_DEPTH];
+};
+
+static int start(void *closure)
+{
+ struct expl *e = closure;
+ e->depth = 0;
+ e->selection = NULL;
+ e->stack[0].cont = NULL;
+ e->stack[0].obj = e->root;
+ e->stack[0].index = 0;
+ e->stack[0].count = 1;
+ return MUSTACH_OK;
+}
+
+static int compare(void *closure, const char *value)
+{
+ struct expl *e = closure;
+ struct json_object *o = e->selection;
+ double d;
+ int64_t i;
+
+ switch (json_object_get_type(o)) {
+ case json_type_double:
+ d = json_object_get_double(o) - atof(value);
+ return d < 0 ? -1 : d > 0 ? 1 : 0;
+ case json_type_int:
+ i = json_object_get_int64(o) - (int64_t)atoll(value);
+ return i < 0 ? -1 : i > 0 ? 1 : 0;
+ default:
+ return strcmp(json_object_get_string(o), value);
+ }
+}
+
+static int sel(void *closure, const char *name)
+{
+ struct expl *e = closure;
+ struct json_object *o;
+ int i, r;
+
+ if (name == NULL) {
+ o = e->stack[e->depth].obj;
+ r = 1;
+ } else {
+ i = e->depth;
+ while (i >= 0 && !json_object_object_get_ex(e->stack[i].obj, name, &o))
+ i--;
+ if (i >= 0)
+ r = 1;
+ else {
+ o = NULL;
+ r = 0;
+ }
+ }
+ e->selection = o;
+ return r;
+}
+
+static int subsel(void *closure, const char *name)
+{
+ struct expl *e = closure;
+ struct json_object *o;
+ int r;
+
+ r = json_object_object_get_ex(e->selection, name, &o);
+ if (r)
+ e->selection = o;
+ return r;
+}
+
+static int enter(void *closure, int objiter)
+{
+ struct expl *e = closure;
+ struct json_object *o;
+
+ if (++e->depth >= MUSTACH_MAX_DEPTH)
+ return MUSTACH_ERROR_TOO_DEEP;
+
+ o = e->selection;
+ e->stack[e->depth].is_objiter = 0;
+ if (objiter) {
+ if (!json_object_is_type(o, json_type_object))
+ goto not_entering;
+
+ e->stack[e->depth].iter = json_object_iter_begin(o);
+ e->stack[e->depth].enditer = json_object_iter_end(o);
+ if (json_object_iter_equal(&e->stack[e->depth].iter, &e->stack[e->depth].enditer))
+ goto not_entering;
+ e->stack[e->depth].obj = json_object_iter_peek_value(&e->stack[e->depth].iter);
+ e->stack[e->depth].cont = o;
+ e->stack[e->depth].is_objiter = 1;
+ } else if (json_object_is_type(o, json_type_array)) {
+ e->stack[e->depth].count = json_object_array_length(o);
+ if (e->stack[e->depth].count == 0)
+ goto not_entering;
+ e->stack[e->depth].cont = o;
+ e->stack[e->depth].obj = json_object_array_get_idx(o, 0);
+ e->stack[e->depth].index = 0;
+ } else if (json_object_is_type(o, json_type_object) || json_object_get_boolean(o)) {
+ e->stack[e->depth].count = 1;
+ e->stack[e->depth].cont = NULL;
+ e->stack[e->depth].obj = o;
+ e->stack[e->depth].index = 0;
+ } else
+ goto not_entering;
+ return 1;
+
+not_entering:
+ e->depth--;
+ return 0;
+}
+
+static int next(void *closure)
+{
+ struct expl *e = closure;
+
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
+
+ if (e->stack[e->depth].is_objiter) {
+ json_object_iter_next(&e->stack[e->depth].iter);
+ if (json_object_iter_equal(&e->stack[e->depth].iter, &e->stack[e->depth].enditer))
+ return 0;
+ e->stack[e->depth].obj = json_object_iter_peek_value(&e->stack[e->depth].iter);
+ return 1;
+ }
+
+ e->stack[e->depth].index++;
+ if (e->stack[e->depth].index >= e->stack[e->depth].count)
+ return 0;
+
+ e->stack[e->depth].obj = json_object_array_get_idx(e->stack[e->depth].cont, e->stack[e->depth].index);
+ return 1;
+}
+
+static int leave(void *closure)
+{
+ struct expl *e = closure;
+
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
+
+ e->depth--;
+ return 0;
+}
+
+static int get(void *closure, struct mustach_sbuf *sbuf, int key)
+{
+ struct expl *e = closure;
+ const char *s;
+
+ if (key)
+ s = e->stack[e->depth].is_objiter
+ ? json_object_iter_peek_name(&e->stack[e->depth].iter)
+ : "";
+ else
+ switch (json_object_get_type(e->selection)) {
+ case json_type_string:
+ s = json_object_get_string(e->selection);
+ break;
+ case json_type_null:
+ s = "";
+ break;
+ default:
+ s = json_object_to_json_string_ext(e->selection, 0);
+ break;
+ }
+ sbuf->value = s;
+ return 1;
+}
+
+const struct mustach_wrap_itf mustach_json_c_wrap_itf = {
+ .start = start,
+ .stop = NULL,
+ .compare = compare,
+ .sel = sel,
+ .subsel = subsel,
+ .enter = enter,
+ .next = next,
+ .leave = leave,
+ .get = get
+};
+
+int mustach_json_c_file(const char *template, size_t length, struct json_object *root, int flags, FILE *file)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_file(template, length, &mustach_json_c_wrap_itf, &e, flags, file);
+}
+
+int mustach_json_c_fd(const char *template, size_t length, struct json_object *root, int flags, int fd)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_fd(template, length, &mustach_json_c_wrap_itf, &e, flags, fd);
+}
+
+int mustach_json_c_mem(const char *template, size_t length, struct json_object *root, int flags, char **result, size_t *size)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_mem(template, length, &mustach_json_c_wrap_itf, &e, flags, result, size);
+}
+
+int mustach_json_c_write(const char *template, size_t length, struct json_object *root, int flags, mustach_write_cb_t *writecb, void *closure)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_write(template, length, &mustach_json_c_wrap_itf, &e, flags, writecb, closure);
+}
+
+int mustach_json_c_emit(const char *template, size_t length, struct json_object *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_emit(template, length, &mustach_json_c_wrap_itf, &e, flags, emitcb, closure);
+}
+
+int fmustach_json_c(const char *template, struct json_object *root, FILE *file)
+{
+ return mustach_json_c_file(template, 0, root, -1, file);
+}
+
+int fdmustach_json_c(const char *template, struct json_object *root, int fd)
+{
+ return mustach_json_c_fd(template, 0, root, -1, fd);
+}
+
+int mustach_json_c(const char *template, struct json_object *root, char **result, size_t *size)
+{
+ return mustach_json_c_mem(template, 0, root, -1, result, size);
+}
+
+int umustach_json_c(const char *template, struct json_object *root, mustach_write_cb_t *writecb, void *closure)
+{
+ return mustach_json_c_write(template, 0, root, -1, writecb, closure);
+}
+
+
diff --git a/src/templating/mustach-json-c.h b/src/templating/mustach-json-c.h
new file mode 100644
index 000000000..50846c6cb
--- /dev/null
+++ b/src/templating/mustach-json-c.h
@@ -0,0 +1,160 @@
+/*
+ Author: José Bollo <jobol@nonadev.net>
+
+ https://gitlab.com/jobol/mustach
+
+ SPDX-License-Identifier: ISC
+*/
+
+#ifndef _mustach_json_c_h_included_
+#define _mustach_json_c_h_included_
+
+/*
+ * mustach-json-c is intended to make integration of json-c
+ * library by providing integrated functions.
+ */
+
+#include <json-c/json.h>
+#include "mustach-wrap.h"
+
+/**
+ * Wrap interface used internally by mustach json-c functions.
+ * Can be used for overriding behaviour.
+ */
+extern const struct mustach_wrap_itf mustach_json_c_wrap_itf;
+
+/**
+ * mustach_json_c_file - Renders the mustache 'template' in 'file' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @file: the file where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_json_c_file(const char *template, size_t length, struct json_object *root, int flags, FILE *file);
+
+/**
+ * mustach_json_c_fd - Renders the mustache 'template' in 'fd' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @fd: the file descriptor number where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_json_c_fd(const char *template, size_t length, struct json_object *root, int flags, int fd);
+
+/**
+ * mustach_json_c_mem - Renders the mustache 'template' in 'result' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @result: the pointer receiving the result when 0 is returned
+ * @size: the size of the returned result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_json_c_mem(const char *template, size_t length, struct json_object *root, int flags, char **result, size_t *size);
+
+/**
+ * mustach_json_c_write - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @writecb: the function that write values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_json_c_write(const char *template, size_t length, struct json_object *root, int flags, mustach_write_cb_t *writecb, void *closure);
+
+/**
+ * mustach_json_c_emit - Renders the mustache 'template' for 'root' to custom emiter 'emitcb' with 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @emitcb: the function that emit values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_json_c_emit(const char *template, size_t length, struct json_object *root, int flags, mustach_emit_cb_t *emitcb, void *closure);
+
+/***************************************************************************
+* compatibility with version before 1.0
+*/
+
+/**
+ * OBSOLETE use mustach_json_c_file
+ *
+ * fmustach_json_c - Renders the mustache 'template' in 'file' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @root: the root json object to render
+ * @file: the file where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+
+DEPRECATED_MUSTACH(extern int fmustach_json_c(const char *template, struct json_object *root, FILE *file));
+
+/**
+ * OBSOLETE use mustach_json_c_fd
+ *
+ * fdmustach_json_c - Renders the mustache 'template' in 'fd' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @root: the root json object to render
+ * @fd: the file descriptor number where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+
+DEPRECATED_MUSTACH(extern int fdmustach_json_c(const char *template, struct json_object *root, int fd));
+
+/**
+ * OBSOLETE use mustach_json_c_mem
+ *
+ * mustach_json_c - Renders the mustache 'template' in 'result' for 'root'.
+ *
+ * @template: the template string to instantiate
+ * @root: the root json object to render
+ * @result: the pointer receiving the result when 0 is returned
+ * @size: the size of the returned result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+
+DEPRECATED_MUSTACH(extern int mustach_json_c(const char *template, struct json_object *root, char **result, size_t *size));
+
+/**
+ * OBSOLETE use mustach_json_c_write
+ *
+ * umustach_json_c - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @root: the root json object to render
+ * @writecb: the function that write values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+typedef mustach_write_cb_t *mustach_json_write_cb;
+DEPRECATED_MUSTACH(extern int umustach_json_c(const char *template, struct json_object *root, mustach_write_cb_t *writecb, void *closure));
+
+#endif
diff --git a/src/templating/mustach-tool.c b/src/templating/mustach-tool.c
index 364e34a84..0c8f44070 100644
--- a/src/templating/mustach-tool.c
+++ b/src/templating/mustach-tool.c
@@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
- Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
@@ -27,7 +16,7 @@
#include <string.h>
#include <libgen.h>
-#include "mustach-json-c.h"
+#include "mustach-wrap.h"
static const size_t BLOCKSIZE = 8192;
@@ -43,16 +32,39 @@ static const char *errors[] = {
"bad unescape tag",
"invalid interface",
"item not found",
- "partial not found"
+ "partial not found",
+ "undefined tag"
};
+static const char *errmsg = 0;
+static int flags = 0;
+static FILE *output = 0;
+
static void help(char *prog)
{
- printf("usage: %s json-file mustach-templates...\n", basename(prog));
+ char *name = basename(prog);
+#define STR_INDIR(x) #x
+#define STR(x) STR_INDIR(x)
+ printf("%s version %s\n", name, STR(VERSION));
+#undef STR
+#undef STR_INDIR
+ printf(
+ "\n"
+ "USAGE:\n"
+ " %s [FLAGS] <json-file> <mustach-templates...>\n"
+ "\n"
+ "FLAGS:\n"
+ " -h, --help Prints help information\n"
+ " -s, --strict Error when a tag is undefined\n"
+ "\n"
+ "ARGS: (if a file is -, read standard input)\n"
+ " <json-file> JSON file with input data\n"
+ " <mustach-templates...> Template files to instantiate\n",
+ name);
exit(0);
}
-static char *readfile(const char *filename)
+static char *readfile(const char *filename, size_t *length)
{
int f;
struct stat s;
@@ -106,50 +118,140 @@ static char *readfile(const char *filename)
} while(rc > 0);
close(f);
+ if (length != NULL)
+ *length = pos;
result[pos] = 0;
return result;
}
+static int load_json(const char *filename);
+static int process(const char *content, size_t length);
+static void close_json();
+
int main(int ac, char **av)
{
- struct json_object *o;
- char *t;
+ char *t, *f;
char *prog = *av;
int s;
+ size_t length;
(void)ac; /* unused */
+ flags = Mustach_With_AllExtensions;
+ output = stdout;
- if (*++av) {
+ for( ++av ; av[0] && av[0][0] == '-' && av[0][1] != 0 ; av++) {
if (!strcmp(*av, "-h") || !strcmp(*av, "--help"))
help(prog);
- if (av[0][0] == '-' && !av[0][1])
- o = json_object_from_fd(0);
- else
- o = json_object_from_file(av[0]);
-#if JSON_C_VERSION_NUM >= 0x000D00
- if (json_util_get_last_err() != NULL) {
- fprintf(stderr, "Bad json: %s (file %s)\n", json_util_get_last_err(), av[0]);
- exit(1);
- }
- else
-#endif
- if (o == NULL) {
- fprintf(stderr, "Aborted: null json (file %s)\n", av[0]);
+ if (!strcmp(*av, "-s") || !strcmp(*av, "--strict"))
+ flags |= Mustach_With_ErrorUndefined;
+ }
+ if (*av) {
+ f = (av[0][0] == '-' && !av[0][1]) ? "/dev/stdin" : av[0];
+ s = load_json(f);
+ if (s < 0) {
+ fprintf(stderr, "Can't load json file %s\n", av[0]);
+ if(errmsg)
+ fprintf(stderr, " reason: %s\n", errmsg);
exit(1);
}
while(*++av) {
- t = readfile(*av);
- s = fmustach_json_c(t, o, stdout);
- if (s != 0) {
+ t = readfile(*av, &length);
+ s = process(t, length);
+ free(t);
+ if (s != MUSTACH_OK) {
s = -s;
if (s < 1 || s >= (int)(sizeof errors / sizeof * errors))
s = 0;
fprintf(stderr, "Template error %s (file %s)\n", errors[s], *av);
}
- free(t);
}
- json_object_put(o);
+ close_json();
+ }
+ return 0;
+}
+
+#define MUSTACH_TOOL_JSON_C 1
+#define MUSTACH_TOOL_JANSSON 2
+#define MUSTACH_TOOL_CJSON 3
+
+#define TOOL MUSTACH_TOOL_JANSSON
+
+#if TOOL == MUSTACH_TOOL_JSON_C
+
+#include "mustach-json-c.h"
+
+static struct json_object *o;
+static int load_json(const char *filename)
+{
+ o = json_object_from_file(filename);
+#if JSON_C_VERSION_NUM >= 0x000D00
+ errmsg = json_util_get_last_err();
+ if (errmsg != NULL)
+ return -1;
+#endif
+ if (o == NULL) {
+ errmsg = "null json";
+ return -1;
+ }
+ return 0;
+}
+static int process(const char *content, size_t length)
+{
+ return mustach_json_c_file(content, length, o, flags, output);
+}
+static void close_json()
+{
+ json_object_put(o);
+}
+
+#elif TOOL == MUSTACH_TOOL_JANSSON
+
+#include "mustach-jansson.h"
+
+static json_t *o;
+static json_error_t e;
+static int load_json(const char *filename)
+{
+ o = json_load_file(filename, JSON_DECODE_ANY, &e);
+ if (o == NULL) {
+ errmsg = e.text;
+ return -1;
}
return 0;
}
+static int process(const char *content, size_t length)
+{
+ return mustach_jansson_file(content, length, o, flags, output);
+}
+static void close_json()
+{
+ json_decref(o);
+}
+
+#elif TOOL == MUSTACH_TOOL_CJSON
+#include "mustach-cjson.h"
+
+static cJSON *o;
+static int load_json(const char *filename)
+{
+ char *t;
+ size_t length;
+
+ t = readfile(filename, &length);
+ o = t ? cJSON_ParseWithLength(t, length) : NULL;
+ free(t);
+ return -!o;
+}
+static int process(const char *content, size_t length)
+{
+ return mustach_cJSON_file(content, length, o, flags, output);
+}
+static void close_json()
+{
+ cJSON_Delete(o);
+}
+
+#else
+#error "no defined json library"
+#endif
diff --git a/src/templating/mustach-wrap.c b/src/templating/mustach-wrap.c
new file mode 100644
index 000000000..75cc9d1f6
--- /dev/null
+++ b/src/templating/mustach-wrap.c
@@ -0,0 +1,456 @@
+/*
+ Author: José Bollo <jobol@nonadev.net>
+
+ https://gitlab.com/jobol/mustach
+
+ SPDX-License-Identifier: ISC
+*/
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <malloc.h>
+#endif
+
+#include "mustach.h"
+#include "mustach-wrap.h"
+
+#if !defined(INCLUDE_PARTIAL_EXTENSION)
+# define INCLUDE_PARTIAL_EXTENSION ".mustache"
+#endif
+
+/* global hook for partials */
+int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf) = NULL;
+
+/* internal structure for wrapping */
+struct wrap {
+ /* original interface */
+ const struct mustach_wrap_itf *itf;
+
+ /* original closure */
+ void *closure;
+
+ /* flags */
+ int flags;
+
+ /* emiter callback */
+ mustach_emit_cb_t *emitcb;
+
+ /* write callback */
+ mustach_write_cb_t *writecb;
+};
+
+/* length given by masking with 3 */
+enum comp {
+ C_no = 0,
+ C_eq = 1,
+ C_lt = 5,
+ C_le = 6,
+ C_gt = 9,
+ C_ge = 10
+};
+
+enum sel {
+ S_none = 0,
+ S_ok = 1,
+ S_objiter = 2,
+ S_ok_or_objiter = S_ok | S_objiter
+};
+
+static enum comp getcomp(char *head, int sflags)
+{
+ return (head[0] == '=' && (sflags & Mustach_With_Equal)) ? C_eq
+ : (head[0] == '<' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_le : C_lt)
+ : (head[0] == '>' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_ge : C_gt)
+ : C_no;
+}
+
+static char *keyval(char *head, int sflags, enum comp *comp)
+{
+ char *w, car, escaped;
+ enum comp k;
+
+ k = C_no;
+ w = head;
+ car = *head;
+ escaped = (sflags & Mustach_With_EscFirstCmp) && (getcomp(head, sflags) != C_no);
+ while (car && (escaped || (k = getcomp(head, sflags)) == C_no)) {
+ if (escaped)
+ escaped = 0;
+ else
+ escaped = ((sflags & Mustach_With_JsonPointer) ? car == '~' : car == '\\')
+ && (getcomp(head + 1, sflags) != C_no);
+ if (!escaped)
+ *w++ = car;
+ head++;
+ car = *head;
+ }
+ *w = 0;
+ *comp = k;
+ return k == C_no ? NULL : &head[k & 3];
+}
+
+static char *getkey(char **head, int sflags)
+{
+ char *result, *iter, *write, car;
+
+ car = *(iter = *head);
+ if (!car)
+ result = NULL;
+ else {
+ result = write = iter;
+ if (sflags & Mustach_With_JsonPointer)
+ {
+ while (car && car != '/') {
+ if (car == '~')
+ switch (iter[1]) {
+ case '1': car = '/'; /*@fallthrough@*/
+ case '0': iter++;
+ }
+ *write++ = car;
+ car = *++iter;
+ }
+ *write = 0;
+ while (car == '/')
+ car = *++iter;
+ }
+ else
+ {
+ while (car && car != '.') {
+ if (car == '\\' && (iter[1] == '.' || iter[1] == '\\'))
+ car = *++iter;
+ *write++ = car;
+ car = *++iter;
+ }
+ *write = 0;
+ while (car == '.')
+ car = *++iter;
+ }
+ *head = iter;
+ }
+ return result;
+}
+
+static enum sel sel(struct wrap *w, const char *name)
+{
+ enum sel result;
+ int i, j, sflags, scmp;
+ char *key, *value;
+ enum comp k;
+
+ /* make a local writeable copy */
+ size_t lenname = 1 + strlen(name);
+ char buffer[lenname];
+ char *copy = buffer;
+ memcpy(copy, name, lenname);
+
+ /* check if matches json pointer selection */
+ sflags = w->flags;
+ if (sflags & Mustach_With_JsonPointer) {
+ if (copy[0] == '/')
+ copy++;
+ else
+ sflags ^= Mustach_With_JsonPointer;
+ }
+
+ /* extract the value, translate the key and get the comparator */
+ if (sflags & (Mustach_With_Equal | Mustach_With_Compare))
+ value = keyval(copy, sflags, &k);
+ else {
+ k = C_no;
+ value = NULL;
+ }
+
+ /* case of . alone if Mustach_With_SingleDot? */
+ if (copy[0] == '.' && copy[1] == 0 /*&& (sflags & Mustach_With_SingleDot)*/)
+ /* yes, select current */
+ result = w->itf->sel(w->closure, NULL) ? S_ok : S_none;
+ else
+ {
+ /* not the single dot, extract the first key */
+ key = getkey(&copy, sflags);
+ if (key == NULL)
+ return 0;
+
+ /* select the root item */
+ if (w->itf->sel(w->closure, key))
+ result = S_ok;
+ else if (key[0] == '*'
+ && !key[1]
+ && !value
+ && !*copy
+ && (w->flags & Mustach_With_ObjectIter)
+ && w->itf->sel(w->closure, NULL))
+ result = S_ok_or_objiter;
+ else
+ result = S_none;
+ if (result == S_ok) {
+ /* iterate the selection of sub items */
+ key = getkey(&copy, sflags);
+ while(result == S_ok && key) {
+ if (w->itf->subsel(w->closure, key))
+ /* nothing */;
+ else if (key[0] == '*'
+ && !key[1]
+ && !value
+ && !*copy
+ && (w->flags & Mustach_With_ObjectIter))
+ result = S_objiter;
+ else
+ result = S_none;
+ key = getkey(&copy, sflags);
+ }
+ }
+ }
+ /* should it be compared? */
+ if (result == S_ok && value) {
+ if (!w->itf->compare)
+ result = S_none;
+ else {
+ i = value[0] == '!';
+ scmp = w->itf->compare(w->closure, &value[i]);
+ switch (k) {
+ case C_eq: j = scmp == 0; break;
+ case C_lt: j = scmp < 0; break;
+ case C_le: j = scmp <= 0; break;
+ case C_gt: j = scmp > 0; break;
+ case C_ge: j = scmp >= 0; break;
+ default: j = i; break;
+ }
+ if (i == j)
+ result = S_none;
+ }
+ }
+ return result;
+}
+
+static int start(void *closure)
+{
+ struct wrap *w = closure;
+ return w->itf->start ? w->itf->start(w->closure) : MUSTACH_OK;
+}
+
+static void stop(void *closure, int status)
+{
+ struct wrap *w = closure;
+ if (w->itf->stop)
+ w->itf->stop(w->closure, status);
+}
+
+static int write(struct wrap *w, const char *buffer, size_t size, FILE *file)
+{
+ int r;
+
+ if (w->writecb)
+ r = w->writecb(file, buffer, size);
+ else
+ r = fwrite(buffer, 1, size, file) == size ? MUSTACH_OK : MUSTACH_ERROR_SYSTEM;
+ return r;
+}
+
+static int emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
+{
+ struct wrap *w = closure;
+ int r;
+ size_t s, i;
+ char car;
+
+ if (w->emitcb)
+ r = w->emitcb(file, buffer, size, escape);
+ else if (!escape)
+ r = write(w, buffer, size, file);
+ else {
+ i = 0;
+ r = MUSTACH_OK;
+ while(i < size && r == MUSTACH_OK) {
+ s = i;
+ while (i < size && (car = buffer[i]) != '<' && car != '>' && car != '&' && car != '"')
+ i++;
+ if (i != s)
+ r = write(w, &buffer[s], i - s, file);
+ if (i < size && r == MUSTACH_OK) {
+ switch(car) {
+ case '<': r = write(w, "&lt;", 4, file); break;
+ case '>': r = write(w, "&gt;", 4, file); break;
+ case '&': r = write(w, "&amp;", 5, file); break;
+ case '"': r = write(w, "&quot;", 6, file); break;
+ }
+ i++;
+ }
+ }
+ }
+ return r;
+}
+
+static int enter(void *closure, const char *name)
+{
+ struct wrap *w = closure;
+ enum sel s = sel(w, name);
+ return s == S_none ? 0 : w->itf->enter(w->closure, s & S_objiter);
+}
+
+static int next(void *closure)
+{
+ struct wrap *w = closure;
+ return w->itf->next(w->closure);
+}
+
+static int leave(void *closure)
+{
+ struct wrap *w = closure;
+ return w->itf->leave(w->closure);
+}
+
+static int getoptional(struct wrap *w, const char *name, struct mustach_sbuf *sbuf)
+{
+ enum sel s = sel(w, name);
+ if (!(s & S_ok))
+ return 0;
+ return w->itf->get(w->closure, sbuf, s & S_objiter);
+}
+
+static int get(void *closure, const char *name, struct mustach_sbuf *sbuf)
+{
+ struct wrap *w = closure;
+ if (getoptional(w, name, sbuf) <= 0) {
+ if (w->flags & Mustach_With_ErrorUndefined)
+ return MUSTACH_ERROR_UNDEFINED_TAG;
+ sbuf->value = "";
+ }
+ return MUSTACH_OK;
+}
+
+static int get_partial_from_file(const char *name, struct mustach_sbuf *sbuf)
+{
+ static char extension[] = INCLUDE_PARTIAL_EXTENSION;
+ size_t s;
+ long pos;
+ FILE *file;
+ char *path, *buffer;
+
+ /* allocate path */
+ s = strlen(name);
+ path = malloc(s + sizeof extension);
+ if (path == NULL)
+ return MUSTACH_ERROR_SYSTEM;
+
+ /* try without extension first */
+ memcpy(path, name, s + 1);
+ file = fopen(path, "r");
+ if (file == NULL) {
+ memcpy(&path[s], extension, sizeof extension);
+ file = fopen(path, "r");
+ }
+ free(path);
+
+ /* if file opened */
+ if (file == NULL)
+ return MUSTACH_ERROR_PARTIAL_NOT_FOUND;
+
+ /* compute file size */
+ if (fseek(file, 0, SEEK_END) >= 0
+ && (pos = ftell(file)) >= 0
+ && fseek(file, 0, SEEK_SET) >= 0) {
+ /* allocate value */
+ s = (size_t)pos;
+ buffer = malloc(s + 1);
+ if (buffer != NULL) {
+ /* read value */
+ if (1 == fread(buffer, s, 1, file)) {
+ /* force zero at end */
+ sbuf->value = buffer;
+ buffer[s] = 0;
+ sbuf->freecb = free;
+ fclose(file);
+ return MUSTACH_OK;
+ }
+ free(buffer);
+ }
+ }
+ fclose(file);
+ return MUSTACH_ERROR_SYSTEM;
+}
+
+static int partial(void *closure, const char *name, struct mustach_sbuf *sbuf)
+{
+ struct wrap *w = closure;
+ int rc;
+ if (mustach_wrap_get_partial != NULL)
+ rc = mustach_wrap_get_partial(name, sbuf);
+ else if (w->flags & Mustach_With_PartialDataFirst) {
+ if (getoptional(w, name, sbuf) > 0)
+ rc = MUSTACH_OK;
+ else
+ rc = get_partial_from_file(name, sbuf);
+ }
+ else {
+ rc = get_partial_from_file(name, sbuf);
+ if (rc != MUSTACH_OK && getoptional(w, name, sbuf) > 0)
+ rc = MUSTACH_OK;
+ }
+ if (rc != MUSTACH_OK)
+ sbuf->value = "";
+ return MUSTACH_OK;
+}
+
+const struct mustach_itf mustach_wrap_itf = {
+ .start = start,
+ .put = NULL,
+ .enter = enter,
+ .next = next,
+ .leave = leave,
+ .partial = partial,
+ .get = get,
+ .emit = emit,
+ .stop = stop
+};
+
+static void wrap_init(struct wrap *wrap, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, mustach_write_cb_t *writecb)
+{
+ if (flags & Mustach_With_Compare)
+ flags |= Mustach_With_Equal;
+ wrap->closure = closure;
+ wrap->itf = itf;
+ wrap->flags = flags;
+ wrap->emitcb = emitcb;
+ wrap->writecb = writecb;
+}
+
+int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, NULL, NULL);
+ return mustach_file(template, length, &mustach_wrap_itf, &w, flags, file);
+}
+
+int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, NULL, NULL);
+ return mustach_fd(template, length, &mustach_wrap_itf, &w, flags, fd);
+}
+
+int mustach_wrap_mem(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, char **result, size_t *size)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, NULL, NULL);
+ return mustach_mem(template, length, &mustach_wrap_itf, &w, flags, result, size);
+}
+
+int mustach_wrap_write(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_write_cb_t *writecb, void *writeclosure)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, NULL, writecb);
+ return mustach_file(template, length, &mustach_wrap_itf, &w, flags, writeclosure);
+}
+
+int mustach_wrap_emit(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, void *emitclosure)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, emitcb, NULL);
+ return mustach_file(template, length, &mustach_wrap_itf, &w, flags, emitclosure);
+}
+
diff --git a/src/templating/mustach-wrap.h b/src/templating/mustach-wrap.h
new file mode 100644
index 000000000..37e6ff6cf
--- /dev/null
+++ b/src/templating/mustach-wrap.h
@@ -0,0 +1,234 @@
+/*
+ Author: José Bollo <jobol@nonadev.net>
+
+ https://gitlab.com/jobol/mustach
+
+ SPDX-License-Identifier: ISC
+*/
+
+#ifndef _mustach_wrap_h_included_
+#define _mustach_wrap_h_included_
+
+/*
+ * mustach-wrap is intended to make integration of JSON
+ * libraries easier by wrapping mustach extensions in a
+ * single place.
+ *
+ * As before, using mustach and only mustach is possible
+ * (by using only mustach.h) but does not implement high
+ * level features coming with extensions implemented by
+ * this high level wrapper.
+ */
+#include "mustach.h"
+/*
+ * Definition of the writing callbacks for mustach functions
+ * producing output to callbacks.
+ *
+ * Two callback types are defined:
+ *
+ * @mustach_write_cb_t:
+ *
+ * callback receiving the escaped data to be written as 3 parameters:
+ *
+ * 1. the 'closure', the same given to the wmustach_... function
+ * 2. a pointer to a 'buffer' containing the characters to be written
+ * 3. the size in bytes of the data pointed by 'buffer'
+ *
+ * @mustach_emit_cb_t:
+ *
+ * callback receiving the data to be written and a flag indicating
+ * if escaping should be done or not as 4 parameters:
+ *
+ * 1. the 'closure', the same given to the emustach_... function
+ * 2. a pointer to a 'buffer' containing the characters to be written
+ * 3. the size in bytes of the data pointed by 'buffer'
+ * 4. a boolean indicating if 'escape' should be done
+ */
+#ifndef _mustach_output_callbacks_defined_
+#define _mustach_output_callbacks_defined_
+typedef int mustach_write_cb_t(void *closure, const char *buffer, size_t size);
+typedef int mustach_emit_cb_t(void *closure, const char *buffer, size_t size, int escape);
+#endif
+
+/**
+ * Flags specific to mustach wrap
+ */
+#define Mustach_With_SingleDot 4 /* obsolete, always set */
+#define Mustach_With_Equal 8
+#define Mustach_With_Compare 16
+#define Mustach_With_JsonPointer 32
+#define Mustach_With_ObjectIter 64
+#define Mustach_With_IncPartial 128 /* obsolete, always set */
+#define Mustach_With_EscFirstCmp 256
+#define Mustach_With_PartialDataFirst 512
+#define Mustach_With_ErrorUndefined 1024
+
+#undef Mustach_With_AllExtensions
+#define Mustach_With_AllExtensions 1023 /* don't include ErrorUndefined */
+
+/**
+ * mustach_wrap_itf - high level wrap of mustach - interface for callbacks
+ *
+ * The functions sel, subsel, enter and next should return 0 or 1.
+ *
+ * All other functions should normally return MUSTACH_OK (zero).
+ *
+ * If any function returns a negative value, it means an error that
+ * stop the processing and that is reported to the caller. Mustach
+ * also has its own error codes. Using the macros MUSTACH_ERROR_USER
+ * and MUSTACH_IS_ERROR_USER could help to avoid clashes.
+ *
+ * @start: If defined (can be NULL), starts the mustach processing
+ * of the closure, called at the very beginning before any
+ * mustach processing occurs.
+ *
+ * @stop: If defined (can be NULL), stops the mustach processing
+ * of the closure, called at the very end after all mustach
+ * processing occurered. The status returned by the processing
+ * is passed to the stop.
+ *
+ * @compare: If defined (can be NULL), compares the value of the
+ * currently selected item with the given value and returns
+ * a negative value if current value is lesser, a positive
+ * value if the current value is greater or zero when
+ * values are equals.
+ * If 'compare' is NULL, any comparison in mustach
+ * is going to fails.
+ *
+ * @sel: Selects the item of the given 'name'. If 'name' is NULL
+ * Selects the current item. Returns 1 if the selection is
+ * effective or else 0 if the selection failed.
+ *
+ * @subsel: Selects from the currently selected object the value of
+ * the field of given name. Returns 1 if the selection is
+ * effective or else 0 if the selection failed.
+ *
+ * @enter: Enters the section of 'name' if possible.
+ * Musts return 1 if entered or 0 if not entered.
+ * When 1 is returned, the function 'leave' will always be called.
+ * Conversely 'leave' is never called when enter returns 0 or
+ * a negative value.
+ * When 1 is returned, the function must activate the first
+ * item of the section.
+ *
+ * @next: Activates the next item of the section if it exists.
+ * Musts return 1 when the next item is activated.
+ * Musts return 0 when there is no item to activate.
+ *
+ * @leave: Leaves the last entered section
+ *
+ * @get: Returns in 'sbuf' the value of the current selection if 'key'
+ * is zero. Otherwise, when 'key' is not zero, return in 'sbuf'
+ * the name of key of the current selection, or if no such key
+ * exists, the empty string. Must return 1 if possible or
+ * 0 when not possible or an error code.
+ */
+struct mustach_wrap_itf {
+ int (*start)(void *closure);
+ void (*stop)(void *closure, int status);
+ int (*compare)(void *closure, const char *value);
+ int (*sel)(void *closure, const char *name);
+ int (*subsel)(void *closure, const char *name);
+ int (*enter)(void *closure, int objiter);
+ int (*next)(void *closure);
+ int (*leave)(void *closure);
+ int (*get)(void *closure, struct mustach_sbuf *sbuf, int key);
+};
+
+/**
+ * Mustach interface used internally by mustach wrapper functions.
+ * Can be used for overriding behaviour.
+ */
+extern const struct mustach_itf mustach_wrap_itf;
+
+/**
+ * Global hook for providing partials. When set to a not NULL value, the pointed
+ * function replaces the default behaviour and is called to provide the partial
+ * of the given 'name' in 'sbuf'.
+ * The function must return MUSTACH_OK when it filled 'sbuf' with value of partial
+ * or must return an error code if it failed.
+ */
+extern int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf);
+
+/**
+ * mustach_wrap_file - Renders the mustache 'template' in 'file' for an abstract
+ * wrapper of interface 'itf' and 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface of the abstract wrapper
+ * @closure: the closure of the abstract wrapper
+ * @file: the file where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file);
+
+/**
+ * mustach_wrap_fd - Renders the mustache 'template' in 'fd' for an abstract
+ * wrapper of interface 'itf' and 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface of the abstract wrapper
+ * @closure: the closure of the abstract wrapper
+ * @fd: the file descriptor number where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd);
+
+/**
+ * mustach_wrap_mem - Renders the mustache 'template' in 'result' for an abstract
+ * wrapper of interface 'itf' and 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface of the abstract wrapper
+ * @closure: the closure of the abstract wrapper
+ * @result: the pointer receiving the result when 0 is returned
+ * @size: the size of the returned result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_wrap_mem(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, char **result, size_t *size);
+
+/**
+ * mustach_wrap_write - Renders the mustache 'template' for an abstract
+ * wrapper of interface 'itf' and 'closure' to custom writer
+ * 'writecb' with 'writeclosure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface of the abstract wrapper
+ * @closure: the closure of the abstract wrapper
+ * @writecb: the function that write values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_wrap_write(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_write_cb_t *writecb, void *writeclosure);
+
+/**
+ * mustach_wrap_emit - Renders the mustache 'template' for an abstract
+ * wrapper of interface 'itf' and 'closure' to custom emiter 'emitcb'
+ * with 'emitclosure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface of the abstract wrapper
+ * @closure: the closure of the abstract wrapper
+ * @emitcb: the function that emit values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_wrap_emit(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, void *emitclosure);
+
+#endif
+
diff --git a/src/templating/mustach.c b/src/templating/mustach.c
index caa80dcc9..548c38224 100644
--- a/src/templating/mustach.c
+++ b/src/templating/mustach.c
@@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
- Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
@@ -27,19 +16,9 @@
#ifdef _WIN32
#include <malloc.h>
#endif
-#ifdef __sun
-# include <alloca.h>
-#endif
#include "mustach.h"
-#if defined(NO_EXTENSION_FOR_MUSTACH)
-# undef NO_COLON_EXTENSION_FOR_MUSTACH
-# define NO_COLON_EXTENSION_FOR_MUSTACH
-# undef NO_ALLOW_EMPTY_TAG
-# define NO_ALLOW_EMPTY_TAG
-#endif
-
struct iwrap {
int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
void *closure; /* closure for: enter, next, leave, emit, get */
@@ -51,6 +30,13 @@ struct iwrap {
int (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
int (*partial)(void *closure, const char *name, struct mustach_sbuf *sbuf);
void *closure_partial; /* closure for partial */
+ int flags;
+};
+
+struct prefix {
+ size_t len;
+ const char *start;
+ struct prefix *prefix;
};
#if !defined(NO_OPEN_MEMSTREAM)
@@ -135,6 +121,7 @@ static inline void sbuf_reset(struct mustach_sbuf *sbuf)
sbuf->value = NULL;
sbuf->freecb = NULL;
sbuf->closure = NULL;
+ sbuf->length = 0;
}
static inline void sbuf_release(struct mustach_sbuf *sbuf)
@@ -143,38 +130,47 @@ static inline void sbuf_release(struct mustach_sbuf *sbuf)
sbuf->releasecb(sbuf->value, sbuf->closure);
}
+static inline size_t sbuf_length(struct mustach_sbuf *sbuf)
+{
+ size_t length = sbuf->length;
+ if (length == 0 && sbuf->value != NULL)
+ length = strlen(sbuf->value);
+ return length;
+}
+
static int iwrap_emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
{
- size_t i, j;
+ size_t i, j, r;
(void)closure; /* unused */
if (!escape)
- return fwrite(buffer, size, 1, file) != 1 ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
+ return fwrite(buffer, 1, size, file) != size ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
- i = 0;
+ r = i = 0;
while (i < size) {
j = i;
- while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&')
+ while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&' && buffer[j] != '"')
j++;
if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
return MUSTACH_ERROR_SYSTEM;
if (j < size) {
switch(buffer[j++]) {
case '<':
- if (fwrite("&lt;", 4, 1, file) != 1)
- return MUSTACH_ERROR_SYSTEM;
+ r = fwrite("&lt;", 4, 1, file);
break;
case '>':
- if (fwrite("&gt;", 4, 1, file) != 1)
- return MUSTACH_ERROR_SYSTEM;
+ r = fwrite("&gt;", 4, 1, file);
break;
case '&':
- if (fwrite("&amp;", 5, 1, file) != 1)
- return MUSTACH_ERROR_SYSTEM;
+ r = fwrite("&amp;", 5, 1, file);
+ break;
+ case '"':
+ r = fwrite("&quot;", 6, 1, file);
break;
- default: break;
}
+ if (r != 1)
+ return MUSTACH_ERROR_SYSTEM;
}
i = j;
}
@@ -191,7 +187,7 @@ static int iwrap_put(void *closure, const char *name, int escape, FILE *file)
sbuf_reset(&sbuf);
rc = iwrap->get(iwrap->closure, name, &sbuf);
if (rc >= 0) {
- length = strlen(sbuf.value);
+ length = sbuf_length(&sbuf);
if (length)
rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
sbuf_release(&sbuf);
@@ -220,55 +216,109 @@ static int iwrap_partial(void *closure, const char *name, struct mustach_sbuf *s
if (rc == 0) {
sbuf->value = result;
sbuf->freecb = free;
+ sbuf->length = size;
}
}
}
return rc;
}
-static int process(const char *template, struct iwrap *iwrap, FILE *file, const char *opstr, const char *clstr)
+static int emitprefix(struct iwrap *iwrap, FILE *file, struct prefix *prefix)
+{
+ if (prefix->prefix) {
+ int rc = emitprefix(iwrap, file, prefix->prefix);
+ if (rc < 0)
+ return rc;
+ }
+ return prefix->len ? iwrap->emit(iwrap->closure, prefix->start, prefix->len, 0, file) : 0;
+}
+
+static int process(const char *template, size_t length, struct iwrap *iwrap, FILE *file, struct prefix *prefix)
{
struct mustach_sbuf sbuf;
- char name[MUSTACH_MAX_LENGTH + 1], c, *tmp;
- const char *beg, *term;
- struct { const char *name, *again; size_t length; int enabled, entered; } stack[MUSTACH_MAX_DEPTH];
+ char opstr[MUSTACH_MAX_DELIM_LENGTH], clstr[MUSTACH_MAX_DELIM_LENGTH];
+ char name[MUSTACH_MAX_LENGTH + 1], c;
+ const char *beg, *term, *end;
+ struct { const char *name, *again; size_t length; unsigned enabled: 1, entered: 1; } stack[MUSTACH_MAX_DEPTH];
size_t oplen, cllen, len, l;
- int depth, rc, enabled;
-
- enabled = 1;
- oplen = strlen(opstr);
- cllen = strlen(clstr);
- depth = 0;
- for(;;) {
- beg = strstr(template, opstr);
- if (beg == NULL) {
- /* no more mustach */
- if (enabled && template[0]) {
- rc = iwrap->emit(iwrap->closure, template, strlen(template), 0, file);
- if (rc < 0)
- return rc;
+ int depth, rc, enabled, stdalone;
+ struct prefix pref;
+
+ pref.prefix = prefix;
+ end = template + (length ? length : strlen(template));
+ opstr[0] = opstr[1] = '{';
+ clstr[0] = clstr[1] = '}';
+ oplen = cllen = 2;
+ stdalone = enabled = 1;
+ depth = pref.len = 0;
+ for (;;) {
+ /* search next openning delimiter */
+ for (beg = template ; ; beg++) {
+ c = beg == end ? '\n' : *beg;
+ if (c == '\n') {
+ l = (beg != end) + (size_t)(beg - template);
+ if (stdalone != 2 && enabled) {
+ if (beg != template /* don't prefix empty lines */) {
+ rc = emitprefix(iwrap, file, &pref);
+ if (rc < 0)
+ return rc;
+ }
+ rc = iwrap->emit(iwrap->closure, template, l, 0, file);
+ if (rc < 0)
+ return rc;
+ }
+ if (beg == end) /* no more mustach */
+ return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
+ template += l;
+ stdalone = 1;
+ pref.len = 0;
+ }
+ else if (!isspace(c)) {
+ if (stdalone == 2 && enabled) {
+ rc = emitprefix(iwrap, file, &pref);
+ if (rc < 0)
+ return rc;
+ pref.len = 0;
+ stdalone = 0;
+ }
+ if (c == *opstr && end - beg >= (ssize_t)oplen) {
+ for (l = 1 ; l < oplen && beg[l] == opstr[l] ; l++);
+ if (l == oplen)
+ break;
+ }
+ stdalone = 0;
}
- return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
- }
- if (enabled && beg != template) {
- rc = iwrap->emit(iwrap->closure, template, (size_t)(beg - template), 0, file);
- if (rc < 0)
- return rc;
}
+
+ pref.start = template;
+ pref.len = enabled ? (size_t)(beg - template) : 0;
beg += oplen;
- term = strstr(beg, clstr);
- if (term == NULL)
- return MUSTACH_ERROR_UNEXPECTED_END;
+
+ /* search next closing delimiter */
+ for (term = beg ; ; term++) {
+ if (term == end)
+ return MUSTACH_ERROR_UNEXPECTED_END;
+ if (*term == *clstr && end - term >= (ssize_t)cllen) {
+ for (l = 1 ; l < cllen && term[l] == clstr[l] ; l++);
+ if (l == cllen)
+ break;
+ }
+ }
template = term + cllen;
len = (size_t)(term - beg);
c = *beg;
switch(c) {
+ case ':':
+ stdalone = 0;
+ if (iwrap->flags & Mustach_With_Colon)
+ goto exclude_first;
+ goto get_name;
case '!':
case '=':
break;
case '{':
- for (l = 0 ; clstr[l] == '}' ; l++);
- if (clstr[l]) {
+ for (l = 0 ; l < cllen && clstr[l] == '}' ; l++);
+ if (l < cllen) {
if (!len || beg[len-1] != '}')
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
len--;
@@ -279,55 +329,63 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
}
c = '&';
/*@fallthrough@*/
+ case '&':
+ stdalone = 0;
+ /*@fallthrough@*/
case '^':
case '#':
case '/':
- case '&':
case '>':
-#if !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
- case ':':
-#endif
- beg++; len--;
+exclude_first:
+ beg++;
+ len--;
+ goto get_name;
default:
+ stdalone = 0;
+get_name:
while (len && isspace(beg[0])) { beg++; len--; }
while (len && isspace(beg[len-1])) len--;
-#if !defined(NO_ALLOW_EMPTY_TAG)
- if (len == 0)
+ if (len == 0 && !(iwrap->flags & Mustach_With_EmptyTag))
return MUSTACH_ERROR_EMPTY_TAG;
-#endif
if (len > MUSTACH_MAX_LENGTH)
return MUSTACH_ERROR_TAG_TOO_LONG;
memcpy(name, beg, len);
name[len] = 0;
break;
}
+ if (stdalone)
+ stdalone = 2;
+ else if (enabled) {
+ rc = emitprefix(iwrap, file, &pref);
+ if (rc < 0)
+ return rc;
+ pref.len = 0;
+ }
switch(c) {
case '!':
/* comment */
/* nothing to do */
break;
case '=':
- /* defines separators */
+ /* defines delimiters */
if (len < 5 || beg[len - 1] != '=')
return MUSTACH_ERROR_BAD_SEPARATORS;
beg++;
len -= 2;
+ while (len && isspace(*beg))
+ beg++, len--;
+ while (len && isspace(beg[len - 1]))
+ len--;
for (l = 0; l < len && !isspace(beg[l]) ; l++);
- if (l == len)
+ if (l == len || l > MUSTACH_MAX_DELIM_LENGTH)
return MUSTACH_ERROR_BAD_SEPARATORS;
oplen = l;
- tmp = alloca(oplen + 1);
- memcpy(tmp, beg, oplen);
- tmp[oplen] = 0;
- opstr = tmp;
+ memcpy(opstr, beg, l);
while (l < len && isspace(beg[l])) l++;
- if (l == len)
+ if (l == len || len - l > MUSTACH_MAX_DELIM_LENGTH)
return MUSTACH_ERROR_BAD_SEPARATORS;
cllen = len - l;
- tmp = alloca(cllen + 1);
- memcpy(tmp, beg + l, cllen);
- tmp[cllen] = 0;
- clstr = tmp;
+ memcpy(clstr, beg + l, cllen);
break;
case '^':
case '#':
@@ -343,8 +401,8 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
stack[depth].name = beg;
stack[depth].again = template;
stack[depth].length = len;
- stack[depth].enabled = enabled;
- stack[depth].entered = rc;
+ stack[depth].enabled = enabled != 0;
+ stack[depth].entered = rc != 0;
if ((c == '#') == (rc == 0))
enabled = 0;
depth++;
@@ -370,7 +428,7 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
sbuf_reset(&sbuf);
rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
if (rc >= 0) {
- rc = process(sbuf.value, iwrap, file, opstr, clstr);
+ rc = process(sbuf.value, sbuf_length(&sbuf), iwrap, file, &pref);
sbuf_release(&sbuf);
}
if (rc < 0)
@@ -389,7 +447,7 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
}
}
-int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file)
+int mustach_file(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, FILE *file)
{
int rc;
struct iwrap iwrap;
@@ -422,17 +480,18 @@ int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE
iwrap.next = itf->next;
iwrap.leave = itf->leave;
iwrap.get = itf->get;
+ iwrap.flags = flags;
/* process */
rc = itf->start ? itf->start(closure) : 0;
if (rc == 0)
- rc = process(template, &iwrap, file, "{{", "}}");
+ rc = process(template, length, &iwrap, file, 0);
if (itf->stop)
itf->stop(closure, rc);
return rc;
}
-int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd)
+int mustach_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd)
{
int rc;
FILE *file;
@@ -442,13 +501,13 @@ int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int
rc = MUSTACH_ERROR_SYSTEM;
errno = ENOMEM;
} else {
- rc = fmustach(template, itf, closure, file);
+ rc = mustach_file(template, length, itf, closure, flags, file);
fclose(file);
}
return rc;
}
-int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size)
+int mustach_mem(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, char **result, size_t *size)
{
int rc;
FILE *file;
@@ -461,7 +520,7 @@ int mustach(const char *template, struct mustach_itf *itf, void *closure, char *
if (file == NULL)
rc = MUSTACH_ERROR_SYSTEM;
else {
- rc = fmustach(template, itf, closure, file);
+ rc = mustach_file(template, length, itf, closure, flags, file);
if (rc < 0)
memfile_abort(file, result, size);
else
@@ -470,3 +529,18 @@ int mustach(const char *template, struct mustach_itf *itf, void *closure, char *
return rc;
}
+int fmustach(const char *template, const struct mustach_itf *itf, void *closure, FILE *file)
+{
+ return mustach_file(template, 0, itf, closure, Mustach_With_AllExtensions, file);
+}
+
+int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd)
+{
+ return mustach_fd(template, 0, itf, closure, Mustach_With_AllExtensions, fd);
+}
+
+int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size)
+{
+ return mustach_mem(template, 0, itf, closure, Mustach_With_AllExtensions, result, size);
+}
+
diff --git a/src/templating/mustach.h b/src/templating/mustach.h
index ad952275c..8c4a43f10 100644
--- a/src/templating/mustach.h
+++ b/src/templating/mustach.h
@@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
- Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ SPDX-License-Identifier: ISC
*/
#ifndef _mustach_h_included_
@@ -25,7 +14,7 @@ struct mustach_sbuf; /* see below */
/**
* Current version of mustach and its derivates
*/
-#define MUSTACH_VERSION 99
+#define MUSTACH_VERSION 102
#define MUSTACH_VERSION_MAJOR (MUSTACH_VERSION / 100)
#define MUSTACH_VERSION_MINOR (MUSTACH_VERSION % 100)
@@ -37,20 +26,59 @@ struct mustach_sbuf; /* see below */
/**
* Maximum length of tags in mustaches {{...}}
*/
-#define MUSTACH_MAX_LENGTH 1024
+#define MUSTACH_MAX_LENGTH 4096
+
+/**
+ * Maximum length of delimitors (2 normally but extended here)
+ */
+#define MUSTACH_MAX_DELIM_LENGTH 8
/**
- * mustach_itf - interface for callbacks
+ * Flags specific to mustach core
+ */
+#define Mustach_With_NoExtensions 0
+#define Mustach_With_Colon 1
+#define Mustach_With_EmptyTag 2
+#define Mustach_With_AllExtensions 3
+
+/*
+ * Definition of error codes returned by mustach
+ */
+#define MUSTACH_OK 0
+#define MUSTACH_ERROR_SYSTEM -1
+#define MUSTACH_ERROR_UNEXPECTED_END -2
+#define MUSTACH_ERROR_EMPTY_TAG -3
+#define MUSTACH_ERROR_TAG_TOO_LONG -4
+#define MUSTACH_ERROR_BAD_SEPARATORS -5
+#define MUSTACH_ERROR_TOO_DEEP -6
+#define MUSTACH_ERROR_CLOSING -7
+#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8
+#define MUSTACH_ERROR_INVALID_ITF -9
+#define MUSTACH_ERROR_ITEM_NOT_FOUND -10
+#define MUSTACH_ERROR_PARTIAL_NOT_FOUND -11
+#define MUSTACH_ERROR_UNDEFINED_TAG -12
+
+/*
+ * You can use definition below for user specific error
*
- * All of this function should return a negative value to stop
- * the mustache processing. The returned negative value will be
- * then returned to the caller of mustach as is.
+ * The macro MUSTACH_ERROR_USER is involutive so for any value
+ * value = MUSTACH_ERROR_USER(MUSTACH_ERROR_USER(value))
+ */
+#define MUSTACH_ERROR_USER_BASE -100
+#define MUSTACH_ERROR_USER(x) (MUSTACH_ERROR_USER_BASE-(x))
+#define MUSTACH_IS_ERROR_USER(x) (MUSTACH_ERROR_USER(x) >= 0)
+
+/**
+ * mustach_itf - pure abstract mustach - interface for callbacks
*
* The functions enter and next should return 0 or 1.
*
* All other functions should normally return MUSTACH_OK (zero).
- * If it returns a negative value, it means an error that stop
- * the process and that is reported to the caller.
+ *
+ * If any function returns a negative value, it means an error that
+ * stop the processing and that is reported to the caller. Mustach
+ * also has its own error codes. Using the macros MUSTACH_ERROR_USER
+ * and MUSTACH_IS_ERROR_USER could help to avoid clashes.
*
* @start: If defined (can be NULL), starts the mustach processing
* of the closure, called at the very beginning before any
@@ -92,18 +120,18 @@ struct mustach_sbuf; /* see below */
* the meaning of 'FILE *file' is abstract for mustach's process and
* then you can use 'FILE*file' pass any kind of pointer (including NULL)
* to the function 'fmustach'. An example of a such behaviour is given by
- * the implementation of 'umustach_json_c'.
+ * the implementation of 'mustach_json_c_write'.
*
* @get: If defined (can be NULL), returns in 'sbuf' the value of 'name'.
* As an extension (see NO_ALLOW_EMPTY_TAG), the 'name' can be
* the empty string. In that later case an implementation can
* return MUSTACH_ERROR_EMPTY_TAG to refuse empty names.
- * If NULL and 'put' NULL the error MUSTACH_ERROR_INVALID_ITF
+ * If 'get' is NULL and 'put' NULL the error MUSTACH_ERROR_INVALID_ITF
* is returned.
*
* @stop: If defined (can be NULL), stops the mustach processing
* of the closure, called at the very end after all mustach
- * processing occurerd. The status returned by the processing
+ * processing occurered. The status returned by the processing
* is passed to the stop.
*
* The array below summarize status of callbacks:
@@ -127,7 +155,7 @@ struct mustach_sbuf; /* see below */
*
* The DUCK case runs on one leg. 'get' is not used if 'partial' is defined
* but is used for 'partial' if 'partial' is NULL. Thus for clarity, do not use
- * it that way but define 'partial' and let 'get' NULL.
+ * it that way but define 'partial' and let 'get' be NULL.
*
* The DANGEROUS case is special: it allows abstract FILE if 'partial' is defined
* but forbids abstract FILE when 'partial' is NULL.
@@ -167,6 +195,9 @@ struct mustach_itf {
* Can be NULL.
*
* @closure: The closure to use for 'releasecb'.
+ *
+ * @length: Length of the value or zero if unknown and value null terminated.
+ * To return the empty string, let it to zero and let value to NULL.
*/
struct mustach_sbuf {
const char *value;
@@ -175,45 +206,84 @@ struct mustach_sbuf {
void (*releasecb)(const char *value, void *closure);
};
void *closure;
+ size_t length;
};
-/*
- * Definition of error codes returned by mustach
+/**
+ * mustach_file - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface to the functions that mustach calls
+ * @closure: the closure to pass to functions called
+ * @file: the file where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
*/
-#define MUSTACH_OK 0
-#define MUSTACH_ERROR_SYSTEM -1
-#define MUSTACH_ERROR_UNEXPECTED_END -2
-#define MUSTACH_ERROR_EMPTY_TAG -3
-#define MUSTACH_ERROR_TAG_TOO_LONG -4
-#define MUSTACH_ERROR_BAD_SEPARATORS -5
-#define MUSTACH_ERROR_TOO_DEEP -6
-#define MUSTACH_ERROR_CLOSING -7
-#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8
-#define MUSTACH_ERROR_INVALID_ITF -9
-#define MUSTACH_ERROR_ITEM_NOT_FOUND -10
-#define MUSTACH_ERROR_PARTIAL_NOT_FOUND -11
-
-/* You can use definition below for user specific error */
-#define MUSTACH_ERROR_USER_BASE -100
-#define MUSTACH_ERROR_USER(x) (MUSTACH_ERROR_USER_BASE-(x))
+extern int mustach_file(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, FILE *file);
/**
- * fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
+ * mustach_fd - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
- * \@file: the file where to write the result
+ * @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file);
+extern int mustach_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd);
/**
- * fmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
+ * mustach_mem - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface to the functions that mustach calls
+ * @closure: the closure to pass to functions called
+ * @result: the pointer receiving the result when 0 is returned
+ * @size: the size of the returned result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_mem(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, char **result, size_t *size);
+
+/***************************************************************************
+* compatibility with version before 1.0
+*/
+#ifdef __GNUC__
+#define DEPRECATED_MUSTACH(func) func __attribute__ ((deprecated))
+#elif defined(_MSC_VER)
+#define DEPRECATED_MUSTACH(func) __declspec(deprecated) func
+#elif !defined(DEPRECATED_MUSTACH)
+#pragma message("WARNING: You need to implement DEPRECATED_MUSTACH for this compiler")
+#define DEPRECATED_MUSTACH(func) func
+#endif
+/**
+ * OBSOLETE use mustach_file
+ *
+ * fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
+ *
+ * @template: the template string to instantiate, null terminated
+ * @itf: the interface to the functions that mustach calls
+ * @closure: the closure to pass to functions called
+ * @file: the file where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+DEPRECATED_MUSTACH(extern int fmustach(const char *template, const struct mustach_itf *itf, void *closure, FILE *file));
+
+/**
+ * OBSOLETE use mustach_fd
+ *
+ * fdmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
+ *
+ * @template: the template string to instantiate, null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @fd: the file descriptor number where to write the result
@@ -221,12 +291,14 @@ extern int fmustach(const char *template, struct mustach_itf *itf, void *closure
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd);
+DEPRECATED_MUSTACH(extern int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd));
/**
- * fmustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
+ * OBSOLETE use mustach_mem
*
- * @template: the template string to instantiate
+ * mustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
+ *
+ * @template: the template string to instantiate, null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @result: the pointer receiving the result when 0 is returned
@@ -235,7 +307,7 @@ extern int fdmustach(const char *template, struct mustach_itf *itf, void *closur
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size);
+DEPRECATED_MUSTACH(extern int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size));
#endif
diff --git a/src/templating/templating_api.c b/src/templating/templating_api.c
index 324e199eb..9261bde79 100644
--- a/src/templating/templating_api.c
+++ b/src/templating/templating_api.c
@@ -180,10 +180,12 @@ TALER_TEMPLATING_fill (const char *tmpl,
int eno;
if (0 !=
- (eno = mustach_jansson (tmpl,
- (json_t *) root,
- (char **) result,
- result_size)))
+ (eno = mustach_jansson_mem (tmpl,
+ 0, /* length of tmpl */
+ (json_t *) root,
+ Mustach_With_NoExtensions,
+ (char **) result,
+ result_size)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"mustach failed on template with error %d\n",
@@ -237,10 +239,12 @@ TALER_TEMPLATING_build (struct MHD_Connection *connection,
GNUNET_free (static_url);
}
if (0 !=
- (eno = mustach_jansson (tmpl,
- (json_t *) root,
- &body,
- &body_size)))
+ (eno = mustach_jansson_mem (tmpl,
+ 0,
+ (json_t *) root,
+ Mustach_With_NoExtensions,
+ &body,
+ &body_size)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"mustach failed on template `%s' with error %d\n",
@@ -428,6 +432,46 @@ load_template (void *cls,
}
+MHD_RESULT
+TALER_TEMPLATING_reply_error (struct MHD_Connection *connection,
+ const char *template_basename,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const char *detail)
+{
+ json_t *data;
+ enum GNUNET_GenericReturnValue ret;
+
+ data = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("ec",
+ ec),
+ GNUNET_JSON_pack_string ("hint",
+ TALER_ErrorCode_get_hint (ec)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("detail",
+ detail))
+ );
+ ret = TALER_TEMPLATING_reply (connection,
+ http_status,
+ template_basename,
+ NULL,
+ NULL,
+ data);
+ json_decref (data);
+ switch (ret)
+ {
+ case GNUNET_OK:
+ return MHD_YES;
+ case GNUNET_NO:
+ return MHD_YES;
+ case GNUNET_SYSERR:
+ return MHD_NO;
+ }
+ GNUNET_assert (0);
+ return MHD_NO;
+}
+
+
enum GNUNET_GenericReturnValue
TALER_TEMPLATING_init (const char *subsystem)
{
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index f0dcecc9e..86cb029c7 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -68,11 +68,13 @@ libtalertesting_la_SOURCES = \
testing_api_cmd_exec_expire.c \
testing_api_cmd_exec_router.c \
testing_api_cmd_exec_transfer.c \
+ testing_api_cmd_exec_wget.c \
testing_api_cmd_exec_wirewatch.c \
testing_api_cmd_insert_deposit.c \
testing_api_cmd_kyc_check_get.c \
testing_api_cmd_kyc_proof.c \
testing_api_cmd_kyc_wallet_get.c \
+ testing_api_cmd_nexus_fetch_transactions.c \
testing_api_cmd_oauth.c \
testing_api_cmd_offline_sign_global_fees.c \
testing_api_cmd_offline_sign_wire_fees.c \
diff --git a/src/testing/test_bank_api.c b/src/testing/test_bank_api.c
index 8cd1ca319..ed2d00353 100644
--- a/src/testing/test_bank_api.c
+++ b/src/testing/test_bank_api.c
@@ -129,6 +129,15 @@ run (void *cls,
TALER_TESTING_cmd_sleep ("Waiting 5s for 'debit-1' to settle",
5),
+ with_libeufin
+ ? TALER_TESTING_cmd_nexus_fetch_transactions (
+ "fetch-transactions-at-nexus",
+ "exchange", /* from taler-nexus-prepare */
+ "x", /* from taler-nexus-prepare */
+ "http://localhost:5001",
+ "my-bank-account") /* from taler-nexus-prepare */
+ : TALER_TESTING_cmd_sleep ("nop",
+ 0),
TALER_TESTING_cmd_bank_debits ("history-2b",
&bc.exchange_auth,
NULL,
diff --git a/src/testing/test_bank_api_twisted.c b/src/testing/test_bank_api_twisted.c
index 80629b6f9..84379ad1f 100644
--- a/src/testing/test_bank_api_twisted.c
+++ b/src/testing/test_bank_api_twisted.c
@@ -85,9 +85,9 @@ run (void *cls,
memset (&wtid,
0x5a,
sizeof (wtid));
- memcpy (&exchange_auth_twisted,
- &bc.exchange_auth,
- sizeof (struct TALER_BANK_AuthenticationData));
+ GNUNET_memcpy (&exchange_auth_twisted,
+ &bc.exchange_auth,
+ sizeof (struct TALER_BANK_AuthenticationData));
if (with_fakebank)
exchange_auth_twisted.wire_gateway_url =
"http://localhost:8888/2/";
diff --git a/src/testing/test_exchange_api_twisted.conf b/src/testing/test_exchange_api-twisted.conf
index 17f8833e7..536d36ee4 100644
--- a/src/testing/test_exchange_api_twisted.conf
+++ b/src/testing/test_exchange_api-twisted.conf
@@ -8,9 +8,6 @@
# only seeks the exchange/BASE_URL URL to connect to the exchange.
BASE_URL = "http://localhost:8888/"
-[bank]
-HTTP_PORT = 8082
-
[twister]
# HTTP listen port for twister
HTTP_PORT = 8888
@@ -30,4 +27,3 @@ ACCEPT_FROM6 = ::1;
UNIXPATH = /tmp/taler-service-twister.sock
UNIX_MATCH_UID = NO
UNIX_MATCH_GID = YES
-
diff --git a/src/testing/test_exchange_api_twisted.c b/src/testing/test_exchange_api_twisted.c
index e89df4647..388c064aa 100644
--- a/src/testing/test_exchange_api_twisted.c
+++ b/src/testing/test_exchange_api_twisted.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2020 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as
@@ -112,8 +112,9 @@ run (void *cls,
* response from a refresh-reveal operation.
*/
struct TALER_TESTING_Command refresh_409_conflict[] = {
- CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve",
- "EUR:5.01"),
+ CMD_TRANSFER_TO_EXCHANGE (
+ "refresh-create-reserve",
+ "EUR:5.01"),
/**
* Make previous command effective.
*/
@@ -121,34 +122,38 @@ run (void *cls,
/**
* Withdraw EUR:5.
*/
- TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin",
- "refresh-create-reserve",
- "EUR:5",
- 0, /* age restriction off */
- MHD_HTTP_OK),
- TALER_TESTING_cmd_deposit ("refresh-deposit-partial",
- "refresh-withdraw-coin",
- 0,
- bc.user42_payto,
- "{\"items\":[{\"name\":\"ice cream\",\
- \"value\":\"EUR:1\"}]}",
- GNUNET_TIME_UNIT_ZERO,
- "EUR:1",
- MHD_HTTP_OK),
+ TALER_TESTING_cmd_withdraw_amount (
+ "refresh-withdraw-coin",
+ "refresh-create-reserve",
+ "EUR:5",
+ 0, /* age restriction off */
+ MHD_HTTP_OK),
+ TALER_TESTING_cmd_deposit (
+ "refresh-deposit-partial",
+ "refresh-withdraw-coin",
+ 0,
+ bc.user42_payto,
+ "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}",
+ GNUNET_TIME_UNIT_ZERO,
+ "EUR:1",
+ MHD_HTTP_OK),
/**
* Melt the rest of the coin's value
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
- TALER_TESTING_cmd_melt ("refresh-melt",
- "refresh-withdraw-coin",
- MHD_HTTP_OK,
- NULL),
+ TALER_TESTING_cmd_melt (
+ "refresh-melt",
+ "refresh-withdraw-coin",
+ MHD_HTTP_OK,
+ NULL),
/* Trigger 409 Conflict. */
- TALER_TESTING_cmd_flip_upload ("flip-upload",
- config_file,
- "transfer_privs.0"),
- TALER_TESTING_cmd_refresh_reveal ("refresh-(flipped-)reveal",
- "refresh-melt",
- MHD_HTTP_CONFLICT),
+ TALER_TESTING_cmd_flip_upload (
+ "flip-upload",
+ config_file,
+ "transfer_privs.0"),
+ TALER_TESTING_cmd_refresh_reveal (
+ "refresh-(flipped-)reveal",
+ "refresh-melt",
+ MHD_HTTP_CONFLICT),
TALER_TESTING_cmd_end ()
};
@@ -159,23 +164,25 @@ run (void *cls,
* lib test suite.
*/
struct TALER_TESTING_Command refund[] = {
- CMD_TRANSFER_TO_EXCHANGE ("create-reserve-r1",
- "EUR:5.01"),
+ CMD_TRANSFER_TO_EXCHANGE (
+ "create-reserve-r1",
+ "EUR:5.01"),
CMD_EXEC_WIREWATCH ("wirewatch-r1"),
- TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1",
- "create-reserve-r1",
- "EUR:5",
- 0, /* age restriction off */
- MHD_HTTP_OK),
- TALER_TESTING_cmd_deposit ("deposit-refund-1",
- "withdraw-coin-r1",
- 0,
- bc.user42_payto,
- "{\"items\":[{\"name\":\"ice cream\","
- "\"value\":\"EUR:5\"}]}",
- GNUNET_TIME_UNIT_MINUTES,
- "EUR:5",
- MHD_HTTP_OK),
+ TALER_TESTING_cmd_withdraw_amount (
+ "withdraw-coin-r1",
+ "create-reserve-r1",
+ "EUR:5",
+ 0, /* age restriction off */
+ MHD_HTTP_OK),
+ TALER_TESTING_cmd_deposit (
+ "deposit-refund-1",
+ "withdraw-coin-r1",
+ 0,
+ bc.user42_payto,
+ "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:5\"}]}",
+ GNUNET_TIME_UNIT_MINUTES,
+ "EUR:5",
+ MHD_HTTP_OK),
TALER_TESTING_cmd_refund ("refund-currency-mismatch",
MHD_HTTP_BAD_REQUEST,
"USD:5",
@@ -190,18 +197,18 @@ run (void *cls,
/* This next deposit CMD is only used to provide a
* good merchant signature to the next (failing) refund
* operations. */
- TALER_TESTING_cmd_deposit ("deposit-refund-to-fail",
- "withdraw-coin-r1",
- 0, /* coin index. */
- bc.user42_payto,
- /* This parameter will make any comparison about
- h_contract_terms fail, when /refund will be handled.
- So in other words, this is h_contract mismatch. */
- "{\"items\":[{\"name\":\"ice skate\","
- "\"value\":\"EUR:5\"}]}",
- GNUNET_TIME_UNIT_MINUTES,
- "EUR:5",
- MHD_HTTP_CONFLICT),
+ TALER_TESTING_cmd_deposit (
+ "deposit-refund-to-fail",
+ "withdraw-coin-r1",
+ 0, /* coin index. */
+ bc.user42_payto,
+ /* This parameter will make any comparison about
+ h_contract_terms fail, when /refund will be handled.
+ So in other words, this is h_contract mismatch. */
+ "{\"items\":[{\"name\":\"ice skate\",\"value\":\"EUR:5\"}]}",
+ GNUNET_TIME_UNIT_MINUTES,
+ "EUR:5",
+ MHD_HTTP_CONFLICT),
TALER_TESTING_cmd_refund ("refund-deposit-not-found",
MHD_HTTP_NOT_FOUND,
"EUR:5",
@@ -219,10 +226,11 @@ run (void *cls,
* are out of date.
*/
struct TALER_TESTING_Command expired_keys[] = {
- TALER_TESTING_cmd_modify_header_dl ("modify-expiration",
- config_file,
- MHD_HTTP_HEADER_EXPIRES,
- "Wed, 19 Jan 586524 08:01:49 GMT"),
+ TALER_TESTING_cmd_modify_header_dl (
+ "modify-expiration",
+ config_file,
+ MHD_HTTP_HEADER_EXPIRES,
+ "Wed, 19 Jan 586524 08:01:49 GMT"),
TALER_TESTING_cmd_check_keys_pull_all_keys (
"check-keys-expiration-0",
2),
@@ -232,28 +240,34 @@ run (void *cls,
CMD_TRANSFER_TO_EXCHANGE ("create-reserve-r2",
"EUR:55.01"),
CMD_EXEC_WIREWATCH ("wirewatch-r2"),
- TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r2",
- "create-reserve-r2",
- "EUR:5",
- 0, /* age restriction off */
- MHD_HTTP_OK),
+ TALER_TESTING_cmd_withdraw_amount (
+ "withdraw-coin-r2",
+ "create-reserve-r2",
+ "EUR:5",
+ 0, /* age restriction off */
+ MHD_HTTP_OK),
TALER_TESTING_cmd_end ()
};
#endif
struct TALER_TESTING_Command commands[] = {
- TALER_TESTING_cmd_wire_add ("add-wire-account",
- "payto://x-taler-bank/localhost/2?receiver-name=2",
- MHD_HTTP_NO_CONTENT,
- false),
- TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
- config_file),
- TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
- 1),
- TALER_TESTING_cmd_batch ("refresh-reveal-409-conflict",
- refresh_409_conflict),
- TALER_TESTING_cmd_batch ("refund",
- refund),
+ TALER_TESTING_cmd_wire_add (
+ "add-wire-account",
+ "payto://x-taler-bank/localhost/2?receiver-name=2",
+ MHD_HTTP_NO_CONTENT,
+ false),
+ TALER_TESTING_cmd_exec_offline_sign_keys (
+ "offline-sign-future-keys",
+ config_file),
+ TALER_TESTING_cmd_check_keys_pull_all_keys (
+ "refetch /keys",
+ 1),
+ TALER_TESTING_cmd_batch (
+ "refresh-reveal-409-conflict",
+ refresh_409_conflict),
+ TALER_TESTING_cmd_batch (
+ "refund",
+ refund),
#if 0
TALER_TESTING_cmd_batch ("expired-keys",
expired_keys),
diff --git a/src/testing/testing_api_cmd_bank_history_credit.c b/src/testing/testing_api_cmd_bank_history_credit.c
index 9a61d6d53..119c5d86c 100644
--- a/src/testing/testing_api_cmd_bank_history_credit.c
+++ b/src/testing/testing_api_cmd_bank_history_credit.c
@@ -386,10 +386,10 @@ history_cb (void *cls,
GNUNET_break (0);
goto error;
case MHD_HTTP_OK:
- for (unsigned int i = 0; i<chr->details.success.details_length; i++)
+ for (unsigned int i = 0; i<chr->details.ok.details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd =
- &chr->details.success.details[i];
+ &chr->details.ok.details[i];
/* check current element */
if (GNUNET_OK !=
diff --git a/src/testing/testing_api_cmd_bank_history_debit.c b/src/testing/testing_api_cmd_bank_history_debit.c
index 33b212ad0..fd1e81994 100644
--- a/src/testing/testing_api_cmd_bank_history_debit.c
+++ b/src/testing/testing_api_cmd_bank_history_debit.c
@@ -378,10 +378,10 @@ history_cb (void *cls,
GNUNET_break (0);
goto error;
case MHD_HTTP_OK:
- for (unsigned int i = 0; i<dhr->details.success.details_length; i++)
+ for (unsigned int i = 0; i<dhr->details.ok.details_length; i++)
{
const struct TALER_BANK_DebitDetails *dd =
- &dhr->details.success.details[i];
+ &dhr->details.ok.details[i];
/* check current element */
if (GNUNET_OK !=
diff --git a/src/testing/testing_api_cmd_batch.c b/src/testing/testing_api_cmd_batch.c
index a5263b03e..d81a77678 100644
--- a/src/testing/testing_api_cmd_batch.c
+++ b/src/testing/testing_api_cmd_batch.c
@@ -150,9 +150,9 @@ TALER_TESTING_cmd_batch (const char *label,
bs->batch = GNUNET_new_array (i + 1,
struct TALER_TESTING_Command);
- memcpy (bs->batch,
- batch,
- sizeof (struct TALER_TESTING_Command) * i);
+ GNUNET_memcpy (bs->batch,
+ batch,
+ sizeof (struct TALER_TESTING_Command) * i);
{
struct TALER_TESTING_Command cmd = {
.cls = bs,
diff --git a/src/testing/testing_api_cmd_batch_deposit.c b/src/testing/testing_api_cmd_batch_deposit.c
index 54a20cdb2..d3275875d 100644
--- a/src/testing/testing_api_cmd_batch_deposit.c
+++ b/src/testing/testing_api_cmd_batch_deposit.c
@@ -212,17 +212,17 @@ batch_deposit_cb (void *cls,
}
if (MHD_HTTP_OK == dr->hr.http_status)
{
- if (ds->num_coins != dr->details.success.num_signatures)
+ if (ds->num_coins != dr->details.ok.num_signatures)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is);
return;
}
ds->deposit_succeeded = GNUNET_YES;
- ds->exchange_timestamp = dr->details.success.deposit_timestamp;
- ds->exchange_pub = *dr->details.success.exchange_pub;
- ds->exchange_sigs = GNUNET_memdup (dr->details.success.exchange_sigs,
- dr->details.success.num_signatures
+ ds->exchange_timestamp = dr->details.ok.deposit_timestamp;
+ ds->exchange_pub = *dr->details.ok.exchange_pub;
+ ds->exchange_sigs = GNUNET_memdup (dr->details.ok.exchange_sigs,
+ dr->details.ok.num_signatures
* sizeof (struct
TALER_ExchangeSignatureP));
}
diff --git a/src/testing/testing_api_cmd_batch_withdraw.c b/src/testing/testing_api_cmd_batch_withdraw.c
index a5229ae9c..2aa549c0f 100644
--- a/src/testing/testing_api_cmd_batch_withdraw.c
+++ b/src/testing/testing_api_cmd_batch_withdraw.c
@@ -210,7 +210,7 @@ reserve_batch_withdraw_cb (void *cls,
{
struct CoinState *cs = &ws->coins[i];
const struct TALER_EXCHANGE_PrivateCoinDetails *pcd
- = &wr->details.success.coins[i];
+ = &wr->details.ok.coins[i];
TALER_denom_sig_deep_copy (&cs->sig,
&pcd->sig);
diff --git a/src/testing/testing_api_cmd_check_aml_decision.c b/src/testing/testing_api_cmd_check_aml_decision.c
index d77e9b6b9..dd317142b 100644
--- a/src/testing/testing_api_cmd_check_aml_decision.c
+++ b/src/testing/testing_api_cmd_check_aml_decision.c
@@ -116,10 +116,10 @@ check_aml_decision_cb (void *cls,
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_amount (ref,
&amount));
- for (unsigned int i = 0; i<adr->details.success.aml_history_length; i++)
+ for (unsigned int i = 0; i<adr->details.ok.aml_history_length; i++)
{
const struct TALER_EXCHANGE_AmlDecisionDetail *aml_history
- = &adr->details.success.aml_history[i];
+ = &adr->details.ok.aml_history[i];
if ( (NULL == oldest) ||
(0 !=
diff --git a/src/testing/testing_api_cmd_contract_get.c b/src/testing/testing_api_cmd_contract_get.c
index 428bf7e8c..d599cb595 100644
--- a/src/testing/testing_api_cmd_contract_get.c
+++ b/src/testing/testing_api_cmd_contract_get.c
@@ -121,7 +121,7 @@ get_cb (void *cls,
const struct TALER_PurseMergePrivateKeyP *mp;
const json_t *ct;
- ds->purse_pub = dr->details.success.purse_pub;
+ ds->purse_pub = dr->details.ok.purse_pub;
if (ds->merge)
{
if (GNUNET_OK !=
@@ -136,8 +136,8 @@ get_cb (void *cls,
TALER_CRYPTO_contract_decrypt_for_merge (
&ds->contract_priv,
&ds->purse_pub,
- dr->details.success.econtract,
- dr->details.success.econtract_size,
+ dr->details.ok.econtract,
+ dr->details.ok.econtract_size,
&ds->merge_priv);
if (0 !=
GNUNET_memcmp (mp,
@@ -153,8 +153,8 @@ get_cb (void *cls,
ds->contract_terms =
TALER_CRYPTO_contract_decrypt_for_deposit (
&ds->contract_priv,
- dr->details.success.econtract,
- dr->details.success.econtract_size);
+ dr->details.ok.econtract,
+ dr->details.ok.econtract_size);
}
if (NULL == ds->contract_terms)
{
diff --git a/src/testing/testing_api_cmd_deposit.c b/src/testing/testing_api_cmd_deposit.c
index 16ac139f4..1b097a346 100644
--- a/src/testing/testing_api_cmd_deposit.c
+++ b/src/testing/testing_api_cmd_deposit.c
@@ -264,9 +264,9 @@ deposit_cb (void *cls,
if (MHD_HTTP_OK == dr->hr.http_status)
{
ds->deposit_succeeded = GNUNET_YES;
- ds->exchange_timestamp = dr->details.success.deposit_timestamp;
- ds->exchange_pub = *dr->details.success.exchange_pub;
- ds->exchange_sig = *dr->details.success.exchange_sig;
+ ds->exchange_timestamp = dr->details.ok.deposit_timestamp;
+ ds->exchange_pub = *dr->details.ok.exchange_pub;
+ ds->exchange_sig = *dr->details.ok.exchange_sig;
}
TALER_TESTING_interpreter_next (ds->is);
}
diff --git a/src/testing/testing_api_cmd_deposits_get.c b/src/testing/testing_api_cmd_deposits_get.c
index 8fd4e813c..c39d7f6c1 100644
--- a/src/testing/testing_api_cmd_deposits_get.c
+++ b/src/testing/testing_api_cmd_deposits_get.c
@@ -128,7 +128,7 @@ deposit_wtid_cb (void *cls,
switch (dr->hr.http_status)
{
case MHD_HTTP_OK:
- tts->wtid = dr->details.success.wtid;
+ tts->wtid = dr->details.ok.wtid;
if (NULL != tts->bank_transfer_reference)
{
const struct TALER_TESTING_Command *bank_transfer_cmd;
@@ -155,7 +155,7 @@ deposit_wtid_cb (void *cls,
}
/* Compare that expected and gotten subjects match. */
- if (0 != GNUNET_memcmp (&dr->details.success.wtid,
+ if (0 != GNUNET_memcmp (&dr->details.ok.wtid,
wtid_want))
{
GNUNET_break (0);
@@ -281,6 +281,7 @@ track_transaction_run (void *cls,
&h_wire_details,
&h_contract_terms,
&coin_pub,
+ GNUNET_TIME_UNIT_ZERO,
&deposit_wtid_cb,
tts);
GNUNET_assert (NULL != tts->tth);
diff --git a/src/testing/testing_api_cmd_exec_wget.c b/src/testing/testing_api_cmd_exec_wget.c
new file mode 100644
index 000000000..67aceca0a
--- /dev/null
+++ b/src/testing/testing_api_cmd_exec_wget.c
@@ -0,0 +1,158 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ 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, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file testing/testing_api_cmd_exec_wget.c
+ * @brief run a wget command
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+/**
+ * State for a "wget" CMD.
+ */
+struct WgetState
+{
+ /**
+ * Process for the wgeter.
+ */
+ struct GNUNET_OS_Process *wget_proc;
+
+ /**
+ * URL to used by the wget.
+ */
+ const char *url;
+};
+
+
+/**
+ * Run the command; use the `wget' program.
+ *
+ * @param cls closure.
+ * @param cmd command currently being executed.
+ * @param is interpreter state.
+ */
+static void
+wget_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct WgetState *ws = cls;
+
+ (void) cmd;
+ ws->wget_proc
+ = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "wget",
+ "wget",
+ ws->url,
+ NULL);
+ if (NULL == ws->wget_proc)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Free the state of a "wget" CMD, and possibly
+ * kills its process if it did not terminate regularly.
+ *
+ * @param cls closure.
+ * @param cmd the command being freed.
+ */
+static void
+wget_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct WgetState *ws = cls;
+
+ (void) cmd;
+ if (NULL != ws->wget_proc)
+ {
+ GNUNET_break (0 ==
+ GNUNET_OS_process_kill (ws->wget_proc,
+ SIGKILL));
+ GNUNET_OS_process_wait (ws->wget_proc);
+ GNUNET_OS_process_destroy (ws->wget_proc);
+ ws->wget_proc = NULL;
+ }
+ GNUNET_free (ws);
+}
+
+
+/**
+ * Offer "wget" CMD internal data to other commands.
+ *
+ * @param cls closure.
+ * @param[out] ret result.
+ * @param trait name of the trait.
+ * @param index index number of the object to offer.
+ * @return #GNUNET_OK on success.
+ */
+static enum GNUNET_GenericReturnValue
+wget_traits (void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct WgetState *ws = cls;
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_process (&ws->wget_proc),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_wget (const char *label,
+ const char *url)
+{
+ struct WgetState *ws;
+
+ ws = GNUNET_new (struct WgetState);
+ ws->url = url;
+
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = ws,
+ .label = label,
+ .run = &wget_run,
+ .cleanup = &wget_cleanup,
+ .traits = &wget_traits
+ };
+
+ return cmd;
+ }
+}
+
+
+/* end of testing_api_cmd_exec_wget.c */
diff --git a/src/testing/testing_api_cmd_nexus_fetch_transactions.c b/src/testing/testing_api_cmd_nexus_fetch_transactions.c
new file mode 100644
index 000000000..ff1497f3f
--- /dev/null
+++ b/src/testing/testing_api_cmd_nexus_fetch_transactions.c
@@ -0,0 +1,187 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ 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, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file testing/testing_api_cmd_nexus_fetch_transactions.c
+ * @brief run a nft command
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+/**
+ * State for a "nft" CMD.
+ */
+struct NftState
+{
+ /**
+ * Process for the nfter.
+ */
+ struct GNUNET_OS_Process *nft_proc;
+
+ const char *username;
+ const char *password;
+ const char *bank_base_url;
+ const char *account_id;
+};
+
+
+/**
+ * Run the command; use the `nft' program.
+ *
+ * @param cls closure.
+ * @param cmd command currently being executed.
+ * @param is interpreter state.
+ */
+static void
+nft_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct NftState *ws = cls;
+ char *url;
+ char *user;
+ char *pass;
+
+ (void) cmd;
+ GNUNET_asprintf (&url,
+ "%s/bank-accounts/%s/fetch-transactions",
+ ws->bank_base_url,
+ ws->account_id);
+ GNUNET_asprintf (&user,
+ "--user=%s",
+ ws->username);
+ GNUNET_asprintf (&pass,
+ "--password=%s",
+ ws->password);
+ ws->nft_proc
+ = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "wget",
+ "wget",
+ "--header=Content-Type:application/json",
+ "--auth-no-challenge",
+ "--output-file=/dev/null",
+ "--post-data={\"level\":\"all\",\"rangeType\":\"latest\"}",
+ user,
+ pass,
+ url,
+ NULL);
+ GNUNET_free (url);
+ GNUNET_free (user);
+ GNUNET_free (pass);
+ if (NULL == ws->nft_proc)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Free the state of a "nft" CMD, and possibly
+ * kills its process if it did not terminate regularly.
+ *
+ * @param cls closure.
+ * @param cmd the command being freed.
+ */
+static void
+nft_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct NftState *ws = cls;
+
+ (void) cmd;
+ if (NULL != ws->nft_proc)
+ {
+ GNUNET_break (0 ==
+ GNUNET_OS_process_kill (ws->nft_proc,
+ SIGKILL));
+ GNUNET_OS_process_wait (ws->nft_proc);
+ GNUNET_OS_process_destroy (ws->nft_proc);
+ ws->nft_proc = NULL;
+ }
+ GNUNET_free (ws);
+}
+
+
+/**
+ * Offer "nft" CMD internal data to other commands.
+ *
+ * @param cls closure.
+ * @param[out] ret result.
+ * @param trait name of the trait.
+ * @param index index number of the object to offer.
+ * @return #GNUNET_OK on success.
+ */
+static enum GNUNET_GenericReturnValue
+nft_traits (void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct NftState *ws = cls;
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_process (&ws->nft_proc),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_nexus_fetch_transactions (
+ const char *label,
+ const char *username,
+ const char *password,
+ const char *bank_base_url,
+ const char *account_id)
+{
+ struct NftState *ws;
+
+ ws = GNUNET_new (struct NftState);
+ ws->username = username;
+ ws->password = password;
+ ws->bank_base_url = bank_base_url;
+ ws->account_id = account_id;
+
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = ws,
+ .label = label,
+ .run = &nft_run,
+ .cleanup = &nft_cleanup,
+ .traits = &nft_traits
+ };
+
+ return cmd;
+ }
+}
+
+
+/* end of testing_api_cmd_nexus_fetch_transactions.c */
diff --git a/src/testing/testing_api_cmd_oauth.c b/src/testing/testing_api_cmd_oauth.c
index 514b4ac8c..0bcf2f680 100644
--- a/src/testing/testing_api_cmd_oauth.c
+++ b/src/testing/testing_api_cmd_oauth.c
@@ -310,6 +310,7 @@ cleanup (void *cls,
(void) toe;
if (NULL == rc)
return;
+ MHD_destroy_post_processor (rc->pp);
GNUNET_free (rc->code);
GNUNET_free (rc->client_id);
GNUNET_free (rc->redirect_uri);
diff --git a/src/testing/testing_api_cmd_purse_create_deposit.c b/src/testing/testing_api_cmd_purse_create_deposit.c
index 200127b77..6fa7d91f9 100644
--- a/src/testing/testing_api_cmd_purse_create_deposit.c
+++ b/src/testing/testing_api_cmd_purse_create_deposit.c
@@ -176,8 +176,8 @@ deposit_cb (void *cls,
}
if (MHD_HTTP_OK == dr->hr.http_status)
{
- ds->exchange_pub = dr->details.success.exchange_pub;
- ds->exchange_sig = dr->details.success.exchange_sig;
+ ds->exchange_pub = dr->details.ok.exchange_pub;
+ ds->exchange_sig = dr->details.ok.exchange_sig;
}
TALER_TESTING_interpreter_next (ds->is);
}
diff --git a/src/testing/testing_api_cmd_purse_deposit.c b/src/testing/testing_api_cmd_purse_deposit.c
index ff8e6d2e3..aaf6ff6ba 100644
--- a/src/testing/testing_api_cmd_purse_deposit.c
+++ b/src/testing/testing_api_cmd_purse_deposit.c
@@ -152,8 +152,8 @@ deposit_cb (void *cls,
if (MHD_HTTP_OK == dr->hr.http_status)
{
if (-1 !=
- TALER_amount_cmp (&dr->details.success.total_deposited,
- &dr->details.success.purse_value_after_fees))
+ TALER_amount_cmp (&dr->details.ok.total_deposited,
+ &dr->details.ok.purse_value_after_fees))
{
const struct TALER_TESTING_Command *purse_cmd;
const struct TALER_ReserveSignatureP *reserve_sig;
@@ -213,7 +213,7 @@ deposit_cb (void *cls,
/* Note: change when flags below changes! */
ds->reserve_history.amount
- = dr->details.success.purse_value_after_fees;
+ = dr->details.ok.purse_value_after_fees;
if (true)
{
ds->reserve_history.details.merge_details.purse_fee = gf->fees.purse;
@@ -226,7 +226,7 @@ deposit_cb (void *cls,
}
}
ds->reserve_history.details.merge_details.h_contract_terms
- = dr->details.success.h_contract_terms;
+ = dr->details.ok.h_contract_terms;
ds->reserve_history.details.merge_details.merge_pub
= *merge_pub;
ds->reserve_history.details.merge_details.purse_pub
@@ -236,7 +236,7 @@ deposit_cb (void *cls,
ds->reserve_history.details.merge_details.merge_timestamp
= *merge_timestamp;
ds->reserve_history.details.merge_details.purse_expiration
- = dr->details.success.purse_expiration;
+ = dr->details.ok.purse_expiration;
ds->reserve_history.details.merge_details.min_age
= ds->min_age;
ds->reserve_history.details.merge_details.flags
diff --git a/src/testing/testing_api_cmd_purse_get.c b/src/testing/testing_api_cmd_purse_get.c
index 3e7da38f0..60638752f 100644
--- a/src/testing/testing_api_cmd_purse_get.c
+++ b/src/testing/testing_api_cmd_purse_get.c
@@ -147,11 +147,11 @@ purse_status_cb (void *cls,
TALER_string_to_amount (ss->expected_balance,
&eb));
if (0 != TALER_amount_cmp (&eb,
- &rs->details.success.balance))
+ &rs->details.ok.balance))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected amount in purse: %s\n",
- TALER_amount_to_string (&rs->details.success.balance));
+ TALER_amount_to_string (&rs->details.ok.balance));
TALER_TESTING_interpreter_fail (ss->is);
return;
}
diff --git a/src/testing/testing_api_cmd_recoup.c b/src/testing/testing_api_cmd_recoup.c
index ed8c7eed7..e11475f20 100644
--- a/src/testing/testing_api_cmd_recoup.c
+++ b/src/testing/testing_api_cmd_recoup.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2018 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as
@@ -73,15 +73,14 @@ struct RecoupState
* was paid back belonged to the right reserve.
*
* @param cls closure
- * @param hr HTTP response details
- * @param reserve_pub public key of the reserve receiving the recoup
+ * @param rr response details
*/
static void
recoup_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_ReservePublicKeyP *reserve_pub)
+ const struct TALER_EXCHANGE_RecoupResponse *rr)
{
struct RecoupState *ps = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
struct TALER_TESTING_Interpreter *is = ps->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
const struct TALER_TESTING_Command *reserve_cmd;
@@ -135,12 +134,6 @@ recoup_cb (void *cls,
{
const struct TALER_ReservePrivateKeyP *reserve_priv;
- if (NULL == reserve_pub)
- {
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
- return;
- }
if (GNUNET_OK !=
TALER_TESTING_get_trait_reserve_priv (reserve_cmd,
&reserve_priv))
@@ -151,7 +144,7 @@ recoup_cb (void *cls,
}
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&ps->reserve_pub.eddsa_pub);
- if (0 != GNUNET_memcmp (reserve_pub,
+ if (0 != GNUNET_memcmp (&rr->details.ok.reserve_pub,
&ps->reserve_pub))
{
GNUNET_break (0);
diff --git a/src/testing/testing_api_cmd_recoup_refresh.c b/src/testing/testing_api_cmd_recoup_refresh.c
index 6081a4ba1..ff7dab004 100644
--- a/src/testing/testing_api_cmd_recoup_refresh.c
+++ b/src/testing/testing_api_cmd_recoup_refresh.c
@@ -73,15 +73,14 @@ struct RecoupRefreshState
* was paid back belonged to the right old coin.
*
* @param cls closure
- * @param hr HTTP response details
- * @param old_coin_pub public key of the dirty coin
+ * @param rrr response details
*/
static void
recoup_refresh_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_CoinSpendPublicKeyP *old_coin_pub)
+ const struct TALER_EXCHANGE_RecoupRefreshResponse *rrr)
{
struct RecoupRefreshState *rrs = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &rrr->hr;
struct TALER_TESTING_Interpreter *is = rrs->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
char *cref;
@@ -150,7 +149,7 @@ recoup_refresh_cb (void *cls,
GNUNET_CRYPTO_eddsa_key_get_public (&dirty_priv->eddsa_priv,
&oc.eddsa_pub);
if (0 != GNUNET_memcmp (&oc,
- old_coin_pub))
+ &rrr->details.ok.old_coin_pub))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
diff --git a/src/testing/testing_api_cmd_refresh.c b/src/testing/testing_api_cmd_refresh.c
index 2aad77ce3..9c2bd8d5e 100644
--- a/src/testing/testing_api_cmd_refresh.c
+++ b/src/testing/testing_api_cmd_refresh.c
@@ -411,7 +411,7 @@ reveal_cb (void *cls,
switch (hr->http_status)
{
case MHD_HTTP_OK:
- rrs->num_fresh_coins = rr->details.success.num_coins;
+ rrs->num_fresh_coins = rr->details.ok.num_coins;
rrs->psa = GNUNET_new_array (rrs->num_fresh_coins,
struct TALER_PlanchetMasterSecretP);
rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins,
@@ -419,7 +419,7 @@ reveal_cb (void *cls,
for (unsigned int i = 0; i<rrs->num_fresh_coins; i++)
{
const struct TALER_EXCHANGE_RevealedCoinInfo *coin
- = &rr->details.success.coins[i];
+ = &rr->details.ok.coins[i];
struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i];
rrs->psa[i] = coin->ps;
@@ -675,11 +675,11 @@ link_cb (void *cls,
TALER_TESTING_interpreter_fail (rls->is);
return;
}
- if (lr->details.success.num_coins != *num_fresh_coins)
+ if (lr->details.ok.num_coins != *num_fresh_coins)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected number of fresh coins: %d vs %d in %s:%u\n",
- lr->details.success.num_coins,
+ lr->details.ok.num_coins,
*num_fresh_coins,
__FILE__,
__LINE__);
@@ -687,11 +687,11 @@ link_cb (void *cls,
return;
}
/* check that the coins match */
- for (unsigned int i = 0; i<lr->details.success.num_coins; i++)
- for (unsigned int j = i + 1; j<lr->details.success.num_coins; j++)
+ for (unsigned int i = 0; i<lr->details.ok.num_coins; i++)
+ for (unsigned int j = i + 1; j<lr->details.ok.num_coins; j++)
if (0 ==
- GNUNET_memcmp (&lr->details.success.coins[i].coin_priv,
- &lr->details.success.coins[j].coin_priv))
+ GNUNET_memcmp (&lr->details.ok.coins[i].coin_priv,
+ &lr->details.ok.coins[j].coin_priv))
GNUNET_break (0);
/* Note: coins might be legitimately permutated in here... */
found = 0;
@@ -709,12 +709,12 @@ link_cb (void *cls,
return;
}
- for (unsigned int i = 0; i<lr->details.success.num_coins; i++)
+ for (unsigned int i = 0; i<lr->details.ok.num_coins; i++)
{
const struct TALER_EXCHANGE_LinkedCoinInfo *lci_i
- = &lr->details.success.coins[i];
+ = &lr->details.ok.coins[i];
- for (unsigned int j = 0; j<lr->details.success.num_coins; j++)
+ for (unsigned int j = 0; j<lr->details.ok.num_coins; j++)
{
const struct TALER_TESTING_FreshCoinData *fcj
= &(*fc)[j];
@@ -735,12 +735,12 @@ link_cb (void *cls,
} /* for j*/
} /* for i */
}
- if (found != lr->details.success.num_coins)
+ if (found != lr->details.ok.num_coins)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Only %u/%u coins match expectations\n",
found,
- lr->details.success.num_coins);
+ lr->details.ok.num_coins);
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
@@ -952,16 +952,16 @@ melt_cb (void *cls,
}
if (MHD_HTTP_OK == hr->http_status)
{
- rms->noreveal_index = mr->details.success.noreveal_index;
- if (mr->details.success.num_mbds != rms->num_fresh_coins)
+ rms->noreveal_index = mr->details.ok.noreveal_index;
+ if (mr->details.ok.num_mbds != rms->num_fresh_coins)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
GNUNET_free (rms->mbds);
- rms->mbds = GNUNET_memdup (mr->details.success.mbds,
- mr->details.success.num_mbds
+ rms->mbds = GNUNET_memdup (mr->details.ok.mbds,
+ mr->details.ok.num_mbds
* sizeof (struct
TALER_EXCHANGE_MeltBlindingDetail));
}
@@ -1239,9 +1239,11 @@ melt_traits (void *cls,
TALER_TESTING_make_trait_h_age_commitment (
index,
rms->refresh_data.melt_h_age_commitment),
- TALER_TESTING_make_trait_exchange_wd_value (index,
- &rms->mbds[index].alg_value),
TALER_TESTING_make_trait_refresh_secret (&rms->rms),
+ (NULL != rms->mbds)
+ ? TALER_TESTING_make_trait_exchange_wd_value (index,
+ &rms->mbds[index].alg_value)
+ : TALER_TESTING_trait_end (),
TALER_TESTING_trait_end ()
};
diff --git a/src/testing/testing_api_cmd_refund.c b/src/testing/testing_api_cmd_refund.c
index 4be3605a4..d41700d1a 100644
--- a/src/testing/testing_api_cmd_refund.c
+++ b/src/testing/testing_api_cmd_refund.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2020 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as
@@ -75,19 +75,14 @@ struct RefundState
* response code is acceptable.
*
* @param cls closure
- * @param hr HTTP response details
- * @param exchange_pub public key the exchange
- * used for signing @a obj.
- * @param exchange_sig actual signature confirming the refund
+ * @param rr response details
*/
static void
refund_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_ExchangePublicKeyP *exchange_pub,
- const struct TALER_ExchangeSignatureP *exchange_sig)
+ const struct TALER_EXCHANGE_RefundResponse *rr)
{
-
struct RefundState *rs = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
struct TALER_TESTING_Command *refund_cmd;
refund_cmd = &rs->is->commands[rs->is->ip];
diff --git a/src/testing/testing_api_cmd_revoke_denom_key.c b/src/testing/testing_api_cmd_revoke_denom_key.c
index 7c77c3566..8afd4f203 100644
--- a/src/testing/testing_api_cmd_revoke_denom_key.c
+++ b/src/testing/testing_api_cmd_revoke_denom_key.c
@@ -65,14 +65,15 @@ struct RevokeState
* Function called with information about the post revocation operation result.
*
* @param cls closure with a `struct RevokeState *`
- * @param hr HTTP response data
+ * @param rdr response data
*/
static void
success_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *rdr)
{
struct RevokeState *rs = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &rdr->hr;
rs->kh = NULL;
if (rs->expected_response_code != hr->http_status)
diff --git a/src/testing/testing_api_cmd_revoke_sign_key.c b/src/testing/testing_api_cmd_revoke_sign_key.c
index 9745d728d..3b869312a 100644
--- a/src/testing/testing_api_cmd_revoke_sign_key.c
+++ b/src/testing/testing_api_cmd_revoke_sign_key.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2020 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as
@@ -65,14 +65,15 @@ struct RevokeState
* Function called with information about the post revocation operation result.
*
* @param cls closure with a `struct RevokeState *`
- * @param hr HTTP response data
+ * @param rsr response data
*/
static void
success_cb (
void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *rsr)
{
struct RevokeState *rs = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &rsr->hr;
rs->kh = NULL;
if (rs->expected_response_code != hr->http_status)
diff --git a/src/testing/testing_api_cmd_set_officer.c b/src/testing/testing_api_cmd_set_officer.c
index 0e6de262f..1c0495f32 100644
--- a/src/testing/testing_api_cmd_set_officer.c
+++ b/src/testing/testing_api_cmd_set_officer.c
@@ -84,13 +84,15 @@ struct SetOfficerState
* if the response code is acceptable.
*
* @param cls closure.
- * @param hr HTTP response details
+ * @param ar response details
*/
static void
set_officer_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct
+ TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar)
{
struct SetOfficerState *ds = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
ds->dh = NULL;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
diff --git a/src/testing/testing_api_cmd_transfer_get.c b/src/testing/testing_api_cmd_transfer_get.c
index 3c467e6da..cb6bb7dfa 100644
--- a/src/testing/testing_api_cmd_transfer_get.c
+++ b/src/testing/testing_api_cmd_transfer_get.c
@@ -115,15 +115,14 @@ track_transfer_cleanup (void *cls,
* wire fees and hashed wire details as well.
*
* @param cls closure.
- * @param hr HTTP response details
- * @param ta transfer data returned by the exchange
+ * @param tgr response details
*/
static void
track_transfer_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_TransferData *ta)
+ const struct TALER_EXCHANGE_TransfersGetResponse *tgr)
{
struct TrackTransferState *tts = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &tgr->hr;
struct TALER_TESTING_Interpreter *is = tts->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
struct TALER_Amount expected_amount;
@@ -148,138 +147,62 @@ track_transfer_cb (void *cls,
switch (hr->http_status)
{
case MHD_HTTP_OK:
- if (NULL == tts->expected_total_amount)
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
- return;
- }
- if (NULL == tts->expected_wire_fee)
- {
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
- return;
- }
-
- if (GNUNET_OK !=
- TALER_string_to_amount (tts->expected_total_amount,
- &expected_amount))
- {
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
- return;
- }
- if (0 != TALER_amount_cmp (&ta->total_amount,
- &expected_amount))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Total amount mismatch to command %s - "
- "%s vs %s\n",
- cmd->label,
- TALER_amount_to_string (&ta->total_amount),
- TALER_amount_to_string (&expected_amount));
- json_dumpf (hr->reply,
- stderr,
- 0);
- fprintf (stderr, "\n");
- TALER_TESTING_interpreter_fail (is);
- return;
- }
-
- if (GNUNET_OK !=
- TALER_string_to_amount (tts->expected_wire_fee,
- &expected_amount))
- {
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
- return;
- }
-
- if (0 != TALER_amount_cmp (&ta->wire_fee,
- &expected_amount))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Wire fee mismatch to command %s\n",
- cmd->label);
- json_dumpf (hr->reply,
- stderr,
- 0);
- TALER_TESTING_interpreter_fail (is);
- return;
- }
+ const struct TALER_EXCHANGE_TransferData *ta
+ = &tgr->details.ok.td;
- /**
- * Optionally checking: (1) wire-details for this transfer
- * match the ones from a referenced "deposit" operation -
- * or any operation that could provide wire-details. (2)
- * Total amount for this transfer matches the one from any
- * referenced command that could provide one.
- */
- if (NULL != tts->wire_details_reference)
- {
- const struct TALER_TESTING_Command *wire_details_cmd;
- const char **payto_uri;
- struct TALER_PaytoHashP h_payto;
-
- wire_details_cmd
- = TALER_TESTING_interpreter_lookup_command (is,
- tts->wire_details_reference);
- if (NULL == wire_details_cmd)
+ if (NULL == tts->expected_total_amount)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
+ if (NULL == tts->expected_wire_fee)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
if (GNUNET_OK !=
- TALER_TESTING_get_trait_payto_uri (wire_details_cmd,
- &payto_uri))
+ TALER_string_to_amount (tts->expected_total_amount,
+ &expected_amount))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
- TALER_payto_hash (*payto_uri,
- &h_payto);
- if (0 != GNUNET_memcmp (&h_payto,
- &ta->h_payto))
+ if (0 != TALER_amount_cmp (&ta->total_amount,
+ &expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Wire hash missmath to command %s\n",
- cmd->label);
+ "Total amount mismatch to command %s - "
+ "%s vs %s\n",
+ cmd->label,
+ TALER_amount_to_string (&ta->total_amount),
+ TALER_amount_to_string (&expected_amount));
json_dumpf (hr->reply,
stderr,
0);
+ fprintf (stderr, "\n");
TALER_TESTING_interpreter_fail (is);
return;
}
- }
- if (NULL != tts->total_amount_reference)
- {
- const struct TALER_TESTING_Command *total_amount_cmd;
- const struct TALER_Amount *total_amount_from_reference;
- total_amount_cmd
- = TALER_TESTING_interpreter_lookup_command (is,
- tts->total_amount_reference);
- if (NULL == total_amount_cmd)
- {
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
- return;
- }
if (GNUNET_OK !=
- TALER_TESTING_get_trait_amount (total_amount_cmd,
- &total_amount_from_reference))
+ TALER_string_to_amount (tts->expected_wire_fee,
+ &expected_amount))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
- if (0 != TALER_amount_cmp (&ta->total_amount,
- total_amount_from_reference))
+
+ if (0 != TALER_amount_cmp (&ta->wire_fee,
+ &expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Amount missmath to command %s\n",
+ "Wire fee mismatch to command %s\n",
cmd->label);
json_dumpf (hr->reply,
stderr,
@@ -287,8 +210,92 @@ track_transfer_cb (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
- }
- }
+
+ /**
+ * Optionally checking: (1) wire-details for this transfer
+ * match the ones from a referenced "deposit" operation -
+ * or any operation that could provide wire-details. (2)
+ * Total amount for this transfer matches the one from any
+ * referenced command that could provide one.
+ */
+ if (NULL != tts->wire_details_reference)
+ {
+ const struct TALER_TESTING_Command *wire_details_cmd;
+ const char **payto_uri;
+ struct TALER_PaytoHashP h_payto;
+
+ wire_details_cmd
+ = TALER_TESTING_interpreter_lookup_command (is,
+ tts->
+ wire_details_reference);
+ if (NULL == wire_details_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_payto_uri (wire_details_cmd,
+ &payto_uri))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ TALER_payto_hash (*payto_uri,
+ &h_payto);
+ if (0 != GNUNET_memcmp (&h_payto,
+ &ta->h_payto))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire hash missmath to command %s\n",
+ cmd->label);
+ json_dumpf (hr->reply,
+ stderr,
+ 0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ }
+ if (NULL != tts->total_amount_reference)
+ {
+ const struct TALER_TESTING_Command *total_amount_cmd;
+ const struct TALER_Amount *total_amount_from_reference;
+
+ total_amount_cmd
+ = TALER_TESTING_interpreter_lookup_command (is,
+ tts->
+ total_amount_reference);
+ if (NULL == total_amount_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_amount (total_amount_cmd,
+ &total_amount_from_reference))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (0 != TALER_amount_cmp (&ta->total_amount,
+ total_amount_from_reference))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Amount missmath to command %s\n",
+ cmd->label);
+ json_dumpf (hr->reply,
+ stderr,
+ 0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ }
+ break;
+ } /* case OK */
+ } /* switch on status */
TALER_TESTING_interpreter_next (is);
}
diff --git a/src/testing/testing_api_cmd_wire.c b/src/testing/testing_api_cmd_wire.c
index 6e44403bc..5fbd41b1e 100644
--- a/src/testing/testing_api_cmd_wire.c
+++ b/src/testing/testing_api_cmd_wire.c
@@ -72,18 +72,14 @@ struct WireState
* that the wire fee is acceptable too.
*
* @param cls closure.
- * @param hr HTTP response details
- * @param accounts_len length of the @a accounts array.
- * @param accounts list of wire accounts of the exchange,
- * NULL on error.
+ * @param wr response details
*/
static void
wire_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- unsigned int accounts_len,
- const struct TALER_EXCHANGE_WireAccount *accounts)
+ const struct TALER_EXCHANGE_WireResponse *wr)
{
struct WireState *ws = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &wr->hr;
struct TALER_TESTING_Command *cmd = &ws->is->commands[ws->is->ip];
struct TALER_Amount expected_fee;
@@ -100,6 +96,15 @@ wire_cb (void *cls,
if (MHD_HTTP_OK == hr->http_status)
{
+ unsigned int accounts_len
+ = wr->details.ok.accounts_len;
+ unsigned int fees_len
+ = wr->details.ok.fees_len;
+ const struct TALER_EXCHANGE_WireAccount *accounts
+ = wr->details.ok.accounts;
+ const struct TALER_EXCHANGE_WireFeesByMethod *fees
+ = wr->details.ok.fees;
+
for (unsigned int i = 0; i<accounts_len; i++)
{
char *method;
@@ -115,32 +120,46 @@ wire_cb (void *cls,
method))
{
ws->method_found = GNUNET_OK;
- if (NULL != ws->expected_fee)
+ }
+ GNUNET_free (method);
+ }
+ if (NULL != ws->expected_fee)
+ {
+ bool fee_found = false;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (ws->expected_fee,
+ &expected_fee));
+ for (unsigned int i = 0; i<fees_len; i++)
+ {
+ if (0 != strcmp (fees[i].method,
+ ws->expected_method))
+ continue;
+ for (const struct TALER_EXCHANGE_WireAggregateFees *waf
+ = fees[i].fees_head;
+ NULL != waf;
+ waf = waf->next)
{
- GNUNET_assert (GNUNET_OK ==
- TALER_string_to_amount (ws->expected_fee,
- &expected_fee));
- for (const struct TALER_EXCHANGE_WireAggregateFees *waf
- = accounts[i].fees;
- NULL != waf;
- waf = waf->next)
+ if (0 != TALER_amount_cmp (&waf->fees.wire,
+ &expected_fee))
{
- if (0 != TALER_amount_cmp (&waf->fees.wire,
- &expected_fee))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Wire fee mismatch to command %s\n",
- cmd->label);
- TALER_TESTING_interpreter_fail (ws->is);
- GNUNET_free (method);
- return;
- }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire fee mismatch to command %s\n",
+ cmd->label);
+ TALER_TESTING_interpreter_fail (ws->is);
+ return;
}
+ fee_found = true;
}
}
- TALER_LOG_DEBUG ("Freeing method '%s'\n",
- method);
- GNUNET_free (method);
+ if (! fee_found)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "/wire does not contain expected fee '%s'\n",
+ ws->expected_fee);
+ TALER_TESTING_interpreter_fail (ws->is);
+ return;
+ }
}
if (GNUNET_OK != ws->method_found)
{
diff --git a/src/testing/testing_api_cmd_wire_add.c b/src/testing/testing_api_cmd_wire_add.c
index c07e9bba6..c36f03b15 100644
--- a/src/testing/testing_api_cmd_wire_add.c
+++ b/src/testing/testing_api_cmd_wire_add.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2020 Taler Systems SA
+ Copyright (C) 2020-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
@@ -67,13 +67,14 @@ struct WireAddState
* if the response code is acceptable.
*
* @param cls closure.
- * @param hr HTTP response details
+ * @param wer response details
*/
static void
wire_add_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer)
{
struct WireAddState *ds = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr;
ds->dh = NULL;
if (ds->expected_response_code != hr->http_status)
@@ -110,10 +111,14 @@ wire_add_run (void *cls,
struct TALER_MasterSignatureP master_sig1;
struct TALER_MasterSignatureP master_sig2;
struct GNUNET_TIME_Timestamp now;
+ json_t *credit_rest;
+ json_t *debit_rest;
(void) cmd;
now = GNUNET_TIME_timestamp_get ();
ds->is = is;
+ debit_rest = json_array ();
+ credit_rest = json_array ();
if (ds->bad_sig)
{
memset (&master_sig1,
@@ -126,10 +131,16 @@ wire_add_run (void *cls,
else
{
TALER_exchange_offline_wire_add_sign (ds->payto_uri,
+ NULL,
+ debit_rest,
+ credit_rest,
now,
&is->master_priv,
&master_sig1);
TALER_exchange_wire_signature_make (ds->payto_uri,
+ NULL,
+ debit_rest,
+ credit_rest,
&is->master_priv,
&master_sig2);
}
@@ -137,11 +148,16 @@ wire_add_run (void *cls,
is->ctx,
is->exchange_url,
ds->payto_uri,
+ NULL,
+ debit_rest,
+ credit_rest,
now,
&master_sig1,
&master_sig2,
&wire_add_cb,
ds);
+ json_decref (debit_rest);
+ json_decref (credit_rest);
if (NULL == ds->dh)
{
GNUNET_break (0);
diff --git a/src/testing/testing_api_cmd_wire_del.c b/src/testing/testing_api_cmd_wire_del.c
index 15d29d727..89fb83957 100644
--- a/src/testing/testing_api_cmd_wire_del.c
+++ b/src/testing/testing_api_cmd_wire_del.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2020 Taler Systems SA
+ Copyright (C) 2020, 2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
@@ -67,13 +67,14 @@ struct WireDelState
* if the response code is acceptable.
*
* @param cls closure.
- * @param hr HTTP response details
+ * @param wdr response details
*/
static void
wire_del_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr)
+ const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdr)
{
struct WireDelState *ds = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &wdr->hr;
ds->dh = NULL;
if (ds->expected_response_code != hr->http_status)
diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c
index 1bd3c187e..8d53f4d07 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -297,10 +297,10 @@ reserve_withdraw_cb (void *cls,
{
case MHD_HTTP_OK:
TALER_denom_sig_deep_copy (&ws->sig,
- &wr->details.success.sig);
- ws->coin_priv = wr->details.success.coin_priv;
- ws->bks = wr->details.success.bks;
- ws->exchange_vals = wr->details.success.exchange_vals;
+ &wr->details.ok.sig);
+ ws->coin_priv = wr->details.ok.coin_priv;
+ ws->bks = wr->details.ok.bks;
+ ws->exchange_vals = wr->details.ok.exchange_vals;
if (0 != ws->total_backoff.rel_value_us)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
diff --git a/src/testing/testing_api_helpers_bank.c b/src/testing/testing_api_helpers_bank.c
index 2507a87e9..f2f92956d 100644
--- a/src/testing/testing_api_helpers_bank.c
+++ b/src/testing/testing_api_helpers_bank.c
@@ -30,6 +30,10 @@
#define BANK_FAIL() \
do {GNUNET_break (0); return NULL; } while (0)
+#define JDBC_TALERCHECK \
+ "jdbc:postgresql://localhost/talercheck?socketFactory=org.newsclub.net.unix." \
+ "AFUNIXSocketFactory$FactoryArg&socketFactoryArg" \
+ "=/var/run/postgresql/.s.PGSQL.5432"
struct TALER_FAKEBANK_Handle *
TALER_TESTING_run_fakebank (const char *bank_url,
@@ -95,7 +99,7 @@ TALER_TESTING_run_libeufin (const struct TALER_TESTING_BankConfiguration *bc)
setenv (
"LIBEUFIN_NEXUS_DB_CONNECTION",
- "jdbc:sqlite:/tmp/libeufin-exchange-test-nexusdb.sqlite3",
+ JDBC_TALERCHECK,
1); // not overwriting any potentially existing DB.
nexus_proc = GNUNET_OS_start_process (
@@ -145,7 +149,7 @@ TALER_TESTING_run_libeufin (const struct TALER_TESTING_BankConfiguration *bc)
fprintf (stderr, "\n");
setenv (
"LIBEUFIN_SANDBOX_DB_CONNECTION",
- "jdbc:sqlite:/tmp/libeufin-exchange-test-sandboxdb.sqlite3",
+ JDBC_TALERCHECK,
1); // not overwriting any potentially existing DB.
setenv (
"LIBEUFIN_SANDBOX_ADMIN_PASSWORD",
@@ -366,7 +370,12 @@ TALER_TESTING_prepare_libeufin (const char *config_filename,
/* DB preparation */
if (reset_db)
{
- if (0 != system ("rm -f /tmp/libeufin-exchange-test-nexusdb.sqlite3"))
+ setenv (
+ "LIBEUFIN_NEXUS_DB_CONNECTION",
+ JDBC_TALERCHECK,
+ 1); // not overwriting any potentially existing DB.
+
+ if (0 != system ("libeufin-nexus reset-tables"))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to invoke db-removal command on nexusdb.\n");
@@ -374,7 +383,13 @@ TALER_TESTING_prepare_libeufin (const char *config_filename,
GNUNET_CONFIGURATION_destroy (cfg);
return GNUNET_SYSERR;
}
- if (0 != system ("rm -f /tmp/libeufin-exchange-test-sandboxdb.sqlite3"))
+
+ setenv (
+ "LIBEUFIN_SANDBOX_DB_CONNECTION",
+ JDBC_TALERCHECK,
+ 1); // not overwriting any potentially existing DB.
+
+ if (0 != system ("libeufin-sandbox reset-tables"))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to invoke db-removal command on sandboxdb.\n");
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index 1865a1129..271b6e768 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -452,9 +452,9 @@ TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is,
is->commands = GNUNET_malloc_large ( (i + 1)
* sizeof (struct TALER_TESTING_Command));
GNUNET_assert (NULL != is->commands);
- memcpy (is->commands,
- commands,
- sizeof (struct TALER_TESTING_Command) * i);
+ GNUNET_memcpy (is->commands,
+ commands,
+ sizeof (struct TALER_TESTING_Command) * i);
is->timeout_task = GNUNET_SCHEDULER_add_delayed (
timeout,
&do_timeout,
@@ -526,49 +526,46 @@ sighandler_child_death (void)
void
TALER_TESTING_cert_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_Keys *keys,
- enum TALER_EXCHANGE_VersionCompatibility compat)
+ const struct TALER_EXCHANGE_KeysResponse *kr)
{
+ const struct TALER_EXCHANGE_HttpResponse *hr = &kr->hr;
struct MainContext *main_ctx = cls;
struct TALER_TESTING_Interpreter *is = main_ctx->is;
- (void) compat;
- if (NULL == keys)
+ switch (hr->http_status)
{
- if (GNUNET_NO == is->working)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Got NULL response for /keys during startup (%u/%d), retrying!\n",
- hr->http_status,
- (int) hr->ec);
- TALER_EXCHANGE_disconnect (is->exchange);
- GNUNET_assert (
- NULL != (is->exchange
- = TALER_EXCHANGE_connect (is->ctx,
- main_ctx->exchange_url,
- &TALER_TESTING_cert_cb,
- main_ctx,
- TALER_EXCHANGE_OPTION_END)));
- return;
- }
- else
+ case MHD_HTTP_OK:
+ /* dealt with below */
+ break;
+ default:
+ if (GNUNET_YES == is->working)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Got NULL response for /keys during execution (%u/%d)!\n",
hr->http_status,
(int) hr->ec);
+ return;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Got failure response for /keys during startup (%u/%d), retrying!\n",
+ hr->http_status,
+ (int) hr->ec);
+ TALER_EXCHANGE_disconnect (is->exchange);
+ GNUNET_assert (
+ NULL != (is->exchange
+ = TALER_EXCHANGE_connect (is->ctx,
+ main_ctx->exchange_url,
+ &TALER_TESTING_cert_cb,
+ main_ctx,
+ TALER_EXCHANGE_OPTION_END)));
+ return;
}
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got %d DK from /keys in generation %u\n",
- keys->num_denom_keys,
- is->key_generation + 1);
- }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got %d DK from /keys in generation %u\n",
+ kr->details.ok.keys->num_denom_keys,
+ is->key_generation + 1);
is->key_generation++;
- is->keys = keys;
+ is->keys = kr->details.ok.keys;
/* /keys has been called for some reason and
* the interpreter is already running. */
diff --git a/src/util/.gitignore b/src/util/.gitignore
index c5f8c76dd..d79786ec7 100644
--- a/src/util/.gitignore
+++ b/src/util/.gitignore
@@ -9,3 +9,4 @@ test_helper_cs
test_helper_cs_home/
test_helper_eddsa
test_helper_eddsa_home/
+test_conversion
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 115ea5055..9d1ec9d42 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -20,7 +20,8 @@ EXTRA_DIST = \
taler-config.in \
test_helper_eddsa.conf \
test_helper_rsa.conf \
- test_helper_cs.conf
+ test_helper_cs.conf \
+ test_conversion.sh
bin_PROGRAMS = \
taler-exchange-secmod-eddsa \
@@ -80,6 +81,7 @@ libtalerutil_la_SOURCES = \
aml_signatures.c \
auditor_signatures.c \
config.c \
+ conversion.c \
crypto.c \
crypto_confirmation.c \
crypto_contract.c \
@@ -125,6 +127,7 @@ AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=
check_PROGRAMS = \
test_age_restriction \
test_amount \
+ test_conversion \
test_crypto \
test_helper_eddsa \
test_helper_rsa \
@@ -141,6 +144,14 @@ test_age_restriction_LDADD = \
-lgnunetutil \
libtalerutil.la
+test_conversion_SOURCES = \
+ test_conversion.c
+test_conversion_LDADD = \
+ -lgnunetjson \
+ -lgnunetutil \
+ -ljansson \
+ libtalerutil.la
+
test_amount_SOURCES = \
test_amount.c
test_amount_LDADD = \
diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c
index beb68e5a6..cf81d915b 100644
--- a/src/util/age_restriction.c
+++ b/src/util/age_restriction.c
@@ -513,6 +513,9 @@ TALER_parse_age_group_string (
unsigned int val = 0;
char c;
+ /* reset mask */
+ mask->bits = 0;
+
while (*pos)
{
c = *pos++;
diff --git a/src/util/amount.c b/src/util/amount.c
index d5698e8b6..9cd0739c9 100644
--- a/src/util/amount.c
+++ b/src/util/amount.c
@@ -72,9 +72,8 @@ TALER_string_to_amount (const char *str,
}
GNUNET_assert (TALER_CURRENCY_LEN > (colon - str));
- memcpy (amount->currency,
- str,
- colon - str);
+ for (unsigned int i = 0; i<colon - str; i++)
+ amount->currency[i] = toupper (str[i]);
/* 0-terminate *and* normalize buffer by setting everything to '\0' */
memset (&amount->currency [colon - str],
0,
@@ -193,9 +192,8 @@ TALER_amount_hton (struct TALER_AmountNBO *res,
TALER_amount_is_valid (d));
res->value = GNUNET_htonll (d->value);
res->fraction = htonl (d->fraction);
- memcpy (res->currency,
- d->currency,
- TALER_CURRENCY_LEN);
+ for (unsigned int i = 0; i<TALER_CURRENCY_LEN; i++)
+ res->currency[i] = toupper (d->currency[i]);
}
@@ -205,9 +203,9 @@ TALER_amount_ntoh (struct TALER_Amount *res,
{
res->value = GNUNET_ntohll (dn->value);
res->fraction = ntohl (dn->fraction);
- memcpy (res->currency,
- dn->currency,
- TALER_CURRENCY_LEN);
+ GNUNET_memcpy (res->currency,
+ dn->currency,
+ TALER_CURRENCY_LEN);
GNUNET_assert (GNUNET_YES ==
TALER_amount_is_valid (res));
}
@@ -225,9 +223,8 @@ TALER_amount_set_zero (const char *cur,
memset (amount,
0,
sizeof (struct TALER_Amount));
- memcpy (amount->currency,
- cur,
- slen);
+ for (unsigned int i = 0; i<slen; i++)
+ amount->currency[i] = toupper (cur[i]);
return GNUNET_OK;
}
@@ -680,9 +677,9 @@ TALER_amount_multiply (struct TALER_Amount *result,
if (GNUNET_SYSERR ==
TALER_amount_normalize (&in))
return TALER_AAR_INVALID_NORMALIZATION_FAILED;
- memcpy (result->currency,
- amount->currency,
- TALER_CURRENCY_LEN);
+ GNUNET_memcpy (result->currency,
+ amount->currency,
+ TALER_CURRENCY_LEN);
if ( (0 == factor) ||
( (0 == in.value) &&
(0 == in.fraction) ) )
diff --git a/src/util/conversion.c b/src/util/conversion.c
new file mode 100644
index 000000000..501ca0145
--- /dev/null
+++ b/src/util/conversion.c
@@ -0,0 +1,402 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ 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, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file conversion.c
+ * @brief helper routines to run some external JSON-to-JSON converter
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include <gnunet/gnunet_util_lib.h>
+
+
+struct TALER_JSON_ExternalConversion
+{
+ /**
+ * Callback to call with the result.
+ */
+ TALER_JSON_JsonCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Handle to the helper process.
+ */
+ struct GNUNET_OS_Process *helper;
+
+ /**
+ * Pipe for the stdin of the @e helper.
+ */
+ struct GNUNET_DISK_FileHandle *chld_stdin;
+
+ /**
+ * Pipe for the stdout of the @e helper.
+ */
+ struct GNUNET_DISK_FileHandle *chld_stdout;
+
+ /**
+ * Handle to wait on the child to terminate.
+ */
+ struct GNUNET_ChildWaitHandle *cwh;
+
+ /**
+ * Task to read JSON output from the child.
+ */
+ struct GNUNET_SCHEDULER_Task *read_task;
+
+ /**
+ * Task to send JSON input to the child.
+ */
+ struct GNUNET_SCHEDULER_Task *write_task;
+
+ /**
+ * Buffer with data we need to send to the helper.
+ */
+ void *write_buf;
+
+ /**
+ * Buffer for reading data from the helper.
+ */
+ void *read_buf;
+
+ /**
+ * Total length of @e write_buf.
+ */
+ size_t write_size;
+
+ /**
+ * Current write position in @e write_buf.
+ */
+ size_t write_pos;
+
+ /**
+ * Current size of @a read_buf.
+ */
+ size_t read_size;
+
+ /**
+ * Current offset in @a read_buf.
+ */
+ size_t read_pos;
+
+};
+
+
+/**
+ * Function called when we can read more data from
+ * the child process.
+ *
+ * @param cls our `struct TALER_JSON_ExternalConversion *`
+ */
+static void
+read_cb (void *cls)
+{
+ struct TALER_JSON_ExternalConversion *ec = cls;
+
+ ec->read_task = NULL;
+ while (1)
+ {
+ ssize_t ret;
+
+ if (ec->read_size == ec->read_pos)
+ {
+ /* Grow input buffer */
+ size_t ns;
+ void *tmp;
+
+ ns = GNUNET_MAX (2 * ec->read_size,
+ 1024);
+ if (ns > GNUNET_MAX_MALLOC_CHECKED)
+ ns = GNUNET_MAX_MALLOC_CHECKED;
+ if (ec->read_size == ns)
+ {
+ /* Helper returned more than 40 MB of data! Stop reading! */
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_file_close (ec->chld_stdin));
+ return;
+ }
+ tmp = GNUNET_malloc_large (ns);
+ if (NULL == tmp)
+ {
+ /* out of memory, also stop reading */
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "malloc");
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_file_close (ec->chld_stdin));
+ return;
+ }
+ GNUNET_memcpy (tmp,
+ ec->read_buf,
+ ec->read_pos);
+ GNUNET_free (ec->read_buf);
+ ec->read_buf = tmp;
+ ec->read_size = ns;
+ }
+ ret = GNUNET_DISK_file_read (ec->chld_stdout,
+ ec->read_buf,
+ ec->read_size - ec->read_pos);
+ if (ret < 0)
+ {
+ if ( (EAGAIN != errno) &&
+ (EWOULDBLOCK != errno) &&
+ (EINTR != errno) )
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "read");
+ return;
+ }
+ break;
+ }
+ if (0 == ret)
+ {
+ /* regular end of stream, good! */
+ return;
+ }
+ GNUNET_assert (ec->read_size >= ec->read_pos + ret);
+ ec->read_pos += ret;
+ }
+ ec->read_task
+ = GNUNET_SCHEDULER_add_read_file (
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ ec->chld_stdout,
+ &read_cb,
+ ec);
+}
+
+
+/**
+ * Function called when we can write more data to
+ * the child process.
+ *
+ * @param cls our `struct TALER_JSON_ExternalConversion *`
+ */
+static void
+write_cb (void *cls)
+{
+ struct TALER_JSON_ExternalConversion *ec = cls;
+ ssize_t ret;
+
+ ec->write_task = NULL;
+ while (ec->write_size > ec->write_pos)
+ {
+ ret = GNUNET_DISK_file_write (ec->chld_stdin,
+ ec->write_buf + ec->write_pos,
+ ec->write_size - ec->write_pos);
+ if (ret < 0)
+ {
+ if ( (EAGAIN != errno) &&
+ (EINTR != errno) )
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "write");
+ break;
+ }
+ if (0 == ret)
+ {
+ GNUNET_break (0);
+ break;
+ }
+ GNUNET_assert (ec->write_size >= ec->write_pos + ret);
+ ec->write_pos += ret;
+ }
+ if ( (ec->write_size > ec->write_pos) &&
+ ( (EAGAIN == errno) ||
+ (EWOULDBLOCK == errno) ||
+ (EINTR == errno) ) )
+ {
+ ec->write_task
+ = GNUNET_SCHEDULER_add_write_file (
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ ec->chld_stdin,
+ &write_cb,
+ ec);
+ }
+ else
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_file_close (ec->chld_stdin));
+ ec->chld_stdin = NULL;
+ }
+}
+
+
+/**
+ * Defines a GNUNET_ChildCompletedCallback which is sent back
+ * upon death or completion of a child process.
+ *
+ * @param cls handle for the callback
+ * @param type type of the process
+ * @param exit_code status code of the process
+ *
+ */
+static void
+child_done_cb (void *cls,
+ enum GNUNET_OS_ProcessStatusType type,
+ long unsigned int exit_code)
+{
+ struct TALER_JSON_ExternalConversion *ec = cls;
+ json_t *j;
+ json_error_t err;
+
+ ec->cwh = NULL;
+ if (NULL != ec->read_task)
+ {
+ GNUNET_SCHEDULER_cancel (ec->read_task);
+ /* We could get the process termination notification before having drained
+ the read buffer. So drain it now, just in case. */
+ read_cb (ec);
+ }
+ if (NULL != ec->read_task)
+ {
+ GNUNET_SCHEDULER_cancel (ec->read_task);
+ ec->read_task = NULL;
+ }
+ GNUNET_OS_process_destroy (ec->helper);
+ ec->helper = NULL;
+ j = json_loadb (ec->read_buf,
+ ec->read_pos,
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == j)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to parse JSON from helper at %d: %s\n",
+ err.position,
+ err.text);
+ }
+ ec->cb (ec->cb_cls,
+ type,
+ exit_code,
+ j);
+ json_decref (j);
+ TALER_JSON_external_conversion_stop (ec);
+}
+
+
+struct TALER_JSON_ExternalConversion *
+TALER_JSON_external_conversion_start (const json_t *input,
+ TALER_JSON_JsonCallback cb,
+ void *cb_cls,
+ const char *binary,
+ ...)
+{
+ struct TALER_JSON_ExternalConversion *ec;
+ struct GNUNET_DISK_PipeHandle *pipe_stdin;
+ struct GNUNET_DISK_PipeHandle *pipe_stdout;
+ va_list ap;
+
+ ec = GNUNET_new (struct TALER_JSON_ExternalConversion);
+ ec->cb = cb;
+ ec->cb_cls = cb_cls;
+ pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
+ GNUNET_assert (NULL != pipe_stdin);
+ pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE);
+ GNUNET_assert (NULL != pipe_stdout);
+ va_start (ap,
+ binary);
+ ec->helper = GNUNET_OS_start_process_va (GNUNET_OS_INHERIT_STD_ERR,
+ pipe_stdin,
+ pipe_stdout,
+ NULL,
+ binary,
+ ap);
+ va_end (ap);
+ if (NULL == ec->helper)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to run conversion helper `%s'\n",
+ binary);
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_pipe_close (pipe_stdin));
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_pipe_close (pipe_stdout));
+ GNUNET_free (ec);
+ return NULL;
+ }
+ ec->chld_stdin =
+ GNUNET_DISK_pipe_detach_end (pipe_stdin,
+ GNUNET_DISK_PIPE_END_WRITE);
+ ec->chld_stdout =
+ GNUNET_DISK_pipe_detach_end (pipe_stdout,
+ GNUNET_DISK_PIPE_END_READ);
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_pipe_close (pipe_stdin));
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_pipe_close (pipe_stdout));
+ ec->write_buf = json_dumps (input, JSON_COMPACT);
+ ec->write_size = strlen (ec->write_buf);
+ ec->read_task
+ = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ ec->chld_stdout,
+ &read_cb,
+ ec);
+ ec->write_task
+ = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ ec->chld_stdin,
+ &write_cb,
+ ec);
+ ec->cwh = GNUNET_wait_child (ec->helper,
+ &child_done_cb,
+ ec);
+ return ec;
+}
+
+
+void
+TALER_JSON_external_conversion_stop (
+ struct TALER_JSON_ExternalConversion *ec)
+{
+ if (NULL != ec->cwh)
+ {
+ GNUNET_wait_child_cancel (ec->cwh);
+ ec->cwh = NULL;
+ }
+ if (NULL != ec->helper)
+ {
+ GNUNET_break (0 ==
+ GNUNET_OS_process_kill (ec->helper,
+ SIGKILL));
+ GNUNET_OS_process_destroy (ec->helper);
+ }
+ if (NULL != ec->read_task)
+ {
+ GNUNET_SCHEDULER_cancel (ec->read_task);
+ ec->read_task = NULL;
+ }
+ if (NULL != ec->write_task)
+ {
+ GNUNET_SCHEDULER_cancel (ec->write_task);
+ ec->write_task = NULL;
+ }
+ if (NULL != ec->chld_stdin)
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_file_close (ec->chld_stdin));
+ ec->chld_stdin = NULL;
+ }
+ if (NULL != ec->chld_stdout)
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_file_close (ec->chld_stdout));
+ ec->chld_stdout = NULL;
+ }
+ GNUNET_free (ec->read_buf);
+ free (ec->write_buf);
+ GNUNET_free (ec);
+}
diff --git a/src/util/crypto_confirmation.c b/src/util/crypto_confirmation.c
index e52562e3e..a238d5376 100644
--- a/src/util/crypto_confirmation.c
+++ b/src/util/crypto_confirmation.c
@@ -90,9 +90,9 @@ compute_totp (struct GNUNET_TIME_Timestamp ts,
mc = gcry_md_read (md,
GCRY_MD_SHA1);
GNUNET_assert (NULL != mc);
- memcpy (hmac,
- mc,
- sizeof (hmac));
+ GNUNET_memcpy (hmac,
+ mc,
+ sizeof (hmac));
gcry_md_close (md);
}
@@ -102,7 +102,7 @@ compute_totp (struct GNUNET_TIME_Timestamp ts,
offset = hmac[sizeof (hmac) - 1] & 0x0f;
for (int count = 0; count < 4; count++)
- code |= hmac[offset + 3 - count] << (8 * count);
+ code |= ((uint32_t) hmac[offset + 3 - count]) << (8 * count);
code &= 0x7fffffff;
/* always use 8 digits (maximum) */
code = code % 100000000;
diff --git a/src/util/crypto_contract.c b/src/util/crypto_contract.c
index 3bfe9eb89..bec34c983 100644
--- a/src/util/crypto_contract.c
+++ b/src/util/crypto_contract.c
@@ -131,9 +131,9 @@ blob_encrypt (const struct NonceP *nonce,
+ data_size;
*res_size = ciphertext_size;
*res = GNUNET_malloc (ciphertext_size);
- memcpy (*res,
- nonce,
- crypto_secretbox_NONCEBYTES);
+ GNUNET_memcpy (*res,
+ nonce,
+ crypto_secretbox_NONCEBYTES);
GNUNET_assert (0 ==
crypto_secretbox_easy (*res + crypto_secretbox_NONCEBYTES,
data,
@@ -274,9 +274,9 @@ TALER_CRYPTO_contract_encrypt_for_merge (
hdr->header.ctype = htonl (TALER_EXCHANGE_CONTRACT_PAYMENT_OFFER);
hdr->header.clen = htonl ((uint32_t) clen);
hdr->merge_priv = *merge_priv;
- memcpy (&hdr[1],
- xbuf,
- cbuf_size);
+ GNUNET_memcpy (&hdr[1],
+ xbuf,
+ cbuf_size);
GNUNET_free (xbuf);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&nonce,
@@ -423,9 +423,9 @@ TALER_CRYPTO_contract_encrypt_for_deposit (
hdr = GNUNET_malloc (sizeof (*hdr) + cbuf_size);
hdr->ctype = htonl (TALER_EXCHANGE_CONTRACT_PAYMENT_REQUEST);
hdr->clen = htonl ((uint32_t) clen);
- memcpy (&hdr[1],
- xbuf,
- cbuf_size);
+ GNUNET_memcpy (&hdr[1],
+ xbuf,
+ cbuf_size);
GNUNET_free (xbuf);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&nonce,
@@ -441,12 +441,12 @@ TALER_CRYPTO_contract_encrypt_for_deposit (
GNUNET_free (hdr);
/* prepend purse_pub */
*econtract = GNUNET_malloc (xecontract_size + sizeof (*purse_pub));
- memcpy (*econtract,
- purse_pub,
- sizeof (*purse_pub));
- memcpy (sizeof (*purse_pub) + *econtract,
- xecontract,
- xecontract_size);
+ GNUNET_memcpy (*econtract,
+ purse_pub,
+ sizeof (*purse_pub));
+ GNUNET_memcpy (sizeof (*purse_pub) + *econtract,
+ xecontract,
+ xecontract_size);
*econtract_size = xecontract_size + sizeof (*purse_pub);
GNUNET_free (xecontract);
}
@@ -573,9 +573,9 @@ TALER_CRYPTO_kyc_attributes_encrypt (
cbuf_size = compressBound (clen);
xbuf = GNUNET_malloc (cbuf_size + sizeof (uint32_t));
belen = htonl ((uint32_t) clen);
- memcpy (xbuf,
- &belen,
- sizeof (belen));
+ GNUNET_memcpy (xbuf,
+ &belen,
+ sizeof (belen));
ret = compress (xbuf + 4,
&cbuf_size,
(const Bytef *) cstr,
@@ -623,9 +623,9 @@ TALER_CRYPTO_kyc_attributes_decrypt (
GNUNET_break_op (0);
return NULL;
}
- memcpy (&belen,
- xhdr,
- sizeof (belen));
+ GNUNET_memcpy (&belen,
+ xhdr,
+ sizeof (belen));
clen = ntohl (belen);
if (clen >= GNUNET_MAX_MALLOC_CHECKED)
{
diff --git a/src/util/crypto_helper_esign.c b/src/util/crypto_helper_esign.c
index 5a9ad74e2..5b04d0ead 100644
--- a/src/util/crypto_helper_esign.c
+++ b/src/util/crypto_helper_esign.c
@@ -357,9 +357,9 @@ TALER_CRYPTO_helper_esign_sign_ (
sr->header.size = htons (sizeof (buf));
sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
sr->reserved = htonl (0);
- memcpy (&sr->purpose,
- purpose,
- purpose_size);
+ GNUNET_memcpy (&sr->purpose,
+ purpose,
+ purpose_size);
if (GNUNET_OK !=
TALER_crypto_helper_send_all (esh->sock,
buf,
diff --git a/src/util/crypto_helper_rsa.c b/src/util/crypto_helper_rsa.c
index efded50a1..4098a846b 100644
--- a/src/util/crypto_helper_rsa.c
+++ b/src/util/crypto_helper_rsa.c
@@ -417,9 +417,9 @@ TALER_CRYPTO_helper_rsa_sign (
sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN);
sr->reserved = htonl (0);
sr->h_rsa = *rsr->h_rsa;
- memcpy (&sr[1],
- rsr->msg,
- rsr->msg_size);
+ GNUNET_memcpy (&sr[1],
+ rsr->msg,
+ rsr->msg_size);
if (GNUNET_OK !=
TALER_crypto_helper_send_all (dh->sock,
buf,
@@ -655,9 +655,9 @@ TALER_CRYPTO_helper_rsa_batch_sign (
sr->header.size = htons (sizeof (*sr) + rsr->msg_size);
sr->reserved = htonl (0);
sr->h_rsa = *rsr->h_rsa;
- memcpy (&sr[1],
- rsr->msg,
- rsr->msg_size);
+ GNUNET_memcpy (&sr[1],
+ rsr->msg,
+ rsr->msg_size);
wbuf += sizeof (*sr) + rsr->msg_size;
}
GNUNET_assert (wbuf == &obuf[mlen]);
diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c
index d8bf716c7..6f8ebdaff 100644
--- a/src/util/exchange_signatures.c
+++ b/src/util/exchange_signatures.c
@@ -1177,10 +1177,10 @@ TALER_exchange_online_denomination_expired_sign (
};
/* strncpy would create a compiler warning */
- memcpy (dua.operation,
- op,
- GNUNET_MIN (sizeof (dua.operation),
- strlen (op)));
+ GNUNET_memcpy (dua.operation,
+ op,
+ GNUNET_MIN (sizeof (dua.operation),
+ strlen (op)));
return scb (&dua.purpose,
pub,
sig);
@@ -1204,10 +1204,10 @@ TALER_exchange_online_denomination_expired_verify (
};
/* strncpy would create a compiler warning */
- memcpy (dua.operation,
- op,
- GNUNET_MIN (sizeof (dua.operation),
- strlen (op)));
+ GNUNET_memcpy (dua.operation,
+ op,
+ GNUNET_MIN (sizeof (dua.operation),
+ strlen (op)));
return
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED,
&dua,
diff --git a/src/util/iban.c b/src/util/iban.c
index efd8c4282..c2274d3cb 100644
--- a/src/util/iban.c
+++ b/src/util/iban.c
@@ -233,9 +233,9 @@ TALER_iban_validate (const char *iban)
return GNUNET_strdup ("IBAN number too short to be valid");
if (len > 34)
return GNUNET_strdup ("IBAN number too long to be valid");
- memcpy (cc, iban, 2);
- memcpy (ibancpy, iban + 4, len - 4);
- memcpy (ibancpy + len - 4, iban, 4);
+ GNUNET_memcpy (cc, iban, 2);
+ GNUNET_memcpy (ibancpy, iban + 4, len - 4);
+ GNUNET_memcpy (ibancpy + len - 4, iban, 4);
ibancpy[len] = '\0';
cc_entry.code = cc;
cc_entry.english = NULL;
diff --git a/src/util/offline_signatures.c b/src/util/offline_signatures.c
index b1e3b93a3..fbff850df 100644
--- a/src/util/offline_signatures.c
+++ b/src/util/offline_signatures.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2020-2022 Taler Systems SA
+ Copyright (C) 2020-2023 Taler Systems SA
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
@@ -677,6 +677,22 @@ struct TALER_MasterAddWirePS
* Hash over the exchange's payto URI.
*/
struct TALER_PaytoHashP h_payto GNUNET_PACKED;
+
+ /**
+ * Hash over the conversion URL, all zeros if there
+ * is no conversion URL.
+ */
+ struct GNUNET_HashCode h_conversion_url;
+
+ /**
+ * Hash over the debit restrictions.
+ */
+ struct GNUNET_HashCode h_debit_restrictions;
+
+ /**
+ * Hash over the credit restrictions.
+ */
+ struct GNUNET_HashCode h_credit_restrictions;
};
GNUNET_NETWORK_STRUCT_END
@@ -685,6 +701,9 @@ GNUNET_NETWORK_STRUCT_END
void
TALER_exchange_offline_wire_add_sign (
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp now,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig)
@@ -697,6 +716,14 @@ TALER_exchange_offline_wire_add_sign (
TALER_payto_hash (payto_uri,
&kv.h_payto);
+ if (NULL != conversion_url)
+ GNUNET_CRYPTO_hash (conversion_url,
+ strlen (conversion_url) + 1,
+ &kv.h_conversion_url);
+ TALER_json_hash (debit_restrictions,
+ &kv.h_debit_restrictions);
+ TALER_json_hash (credit_restrictions,
+ &kv.h_credit_restrictions);
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
&kv,
&master_sig->eddsa_signature);
@@ -706,6 +733,9 @@ TALER_exchange_offline_wire_add_sign (
enum GNUNET_GenericReturnValue
TALER_exchange_offline_wire_add_verify (
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
struct GNUNET_TIME_Timestamp sign_time,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig)
@@ -718,6 +748,14 @@ TALER_exchange_offline_wire_add_verify (
TALER_payto_hash (payto_uri,
&aw.h_payto);
+ if (NULL != conversion_url)
+ GNUNET_CRYPTO_hash (conversion_url,
+ strlen (conversion_url) + 1,
+ &aw.h_conversion_url);
+ TALER_json_hash (debit_restrictions,
+ &aw.h_debit_restrictions);
+ TALER_json_hash (credit_restrictions,
+ &aw.h_credit_restrictions);
return
GNUNET_CRYPTO_eddsa_verify (
TALER_SIGNATURE_MASTER_ADD_WIRE,
@@ -967,9 +1005,9 @@ TALER_exchange_offline_global_fee_sign (
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig)
{
- struct TALER_MasterGlobalFeePS kv = {
+ struct TALER_MasterGlobalFeePS wf = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_GLOBAL_FEES),
- .purpose.size = htonl (sizeof (kv)),
+ .purpose.size = htonl (sizeof (wf)),
.start_date = GNUNET_TIME_timestamp_hton (start_time),
.end_date = GNUNET_TIME_timestamp_hton (end_time),
.purse_timeout = GNUNET_TIME_relative_hton (purse_timeout),
@@ -977,10 +1015,10 @@ TALER_exchange_offline_global_fee_sign (
.purse_account_limit = htonl (purse_account_limit)
};
- TALER_global_fee_set_hton (&kv.fees,
+ TALER_global_fee_set_hton (&wf.fees,
fees);
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
- &kv,
+ &wf,
&master_sig->eddsa_signature);
}
@@ -1095,6 +1133,22 @@ struct TALER_MasterWireDetailsPS
*/
struct TALER_PaytoHashP h_wire_details GNUNET_PACKED;
+ /**
+ * Hash over the conversion URL, all zeros if there
+ * is no conversion URL.
+ */
+ struct GNUNET_HashCode h_conversion_url;
+
+ /**
+ * Hash over the debit restrictions.
+ */
+ struct GNUNET_HashCode h_debit_restrictions;
+
+ /**
+ * Hash over the credit restrictions.
+ */
+ struct GNUNET_HashCode h_credit_restrictions;
+
};
GNUNET_NETWORK_STRUCT_END
@@ -1103,6 +1157,9 @@ GNUNET_NETWORK_STRUCT_END
enum GNUNET_GenericReturnValue
TALER_exchange_wire_signature_check (
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig)
{
@@ -1113,6 +1170,14 @@ TALER_exchange_wire_signature_check (
TALER_payto_hash (payto_uri,
&wd.h_wire_details);
+ if (NULL != conversion_url)
+ GNUNET_CRYPTO_hash (conversion_url,
+ strlen (conversion_url) + 1,
+ &wd.h_conversion_url);
+ TALER_json_hash (debit_restrictions,
+ &wd.h_debit_restrictions);
+ TALER_json_hash (credit_restrictions,
+ &wd.h_credit_restrictions);
return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_DETAILS,
&wd,
&master_sig->eddsa_signature,
@@ -1123,6 +1188,9 @@ TALER_exchange_wire_signature_check (
void
TALER_exchange_wire_signature_make (
const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig)
{
@@ -1133,6 +1201,14 @@ TALER_exchange_wire_signature_make (
TALER_payto_hash (payto_uri,
&wd.h_wire_details);
+ if (NULL != conversion_url)
+ GNUNET_CRYPTO_hash (conversion_url,
+ strlen (conversion_url) + 1,
+ &wd.h_conversion_url);
+ TALER_json_hash (debit_restrictions,
+ &wd.h_debit_restrictions);
+ TALER_json_hash (credit_restrictions,
+ &wd.h_credit_restrictions);
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
&wd,
&master_sig->eddsa_signature);
diff --git a/src/util/payto.c b/src/util/payto.c
index 81664b1df..9b0e83e85 100644
--- a/src/util/payto.c
+++ b/src/util/payto.c
@@ -267,9 +267,9 @@ TALER_payto_hash (const char *payto,
&sha512);
GNUNET_static_assert (sizeof (sha512) > sizeof (*h_payto));
/* truncate */
- memcpy (h_payto,
- &sha512,
- sizeof (*h_payto));
+ GNUNET_memcpy (h_payto,
+ &sha512,
+ sizeof (*h_payto));
}
diff --git a/src/util/taler-exchange-secmod-cs.c b/src/util/taler-exchange-secmod-cs.c
index 2cdf09adf..ed0eba15d 100644
--- a/src/util/taler-exchange-secmod-cs.c
+++ b/src/util/taler-exchange-secmod-cs.c
@@ -423,9 +423,9 @@ generate_response (struct DenominationKey *dk)
&an->secm_sig);
an->secm_pub = TES_smpub;
p = (void *) &an[1];
- memcpy (p,
- denom->section,
- nlen);
+ GNUNET_memcpy (p,
+ denom->section,
+ nlen);
dk->an = an;
}
@@ -1373,9 +1373,9 @@ cs_client_init (struct TES_Client *client)
NULL != dk;
dk = dk->next)
{
- memcpy (&buf[obs],
- dk->an,
- ntohs (dk->an->header.size));
+ GNUNET_memcpy (&buf[obs],
+ dk->an,
+ ntohs (dk->an->header.size));
obs += ntohs (dk->an->header.size);
}
}
@@ -1472,18 +1472,18 @@ cs_update_client_keys (struct TES_Client *client)
.h_cs = key->h_cs
};
- memcpy (&buf[obs],
- &pn,
- sizeof (pn));
+ GNUNET_memcpy (&buf[obs],
+ &pn,
+ sizeof (pn));
GNUNET_assert (obs + sizeof (pn)
> obs);
obs += sizeof (pn);
}
else
{
- memcpy (&buf[obs],
- key->an,
- ntohs (key->an->header.size));
+ GNUNET_memcpy (&buf[obs],
+ key->an,
+ ntohs (key->an->header.size));
GNUNET_assert (obs + ntohs (key->an->header.size)
> obs);
obs += ntohs (key->an->header.size);
diff --git a/src/util/taler-exchange-secmod-eddsa.c b/src/util/taler-exchange-secmod-eddsa.c
index e07e9a71d..3b78e71df 100644
--- a/src/util/taler-exchange-secmod-eddsa.c
+++ b/src/util/taler-exchange-secmod-eddsa.c
@@ -826,9 +826,9 @@ parse_key (const char *filename,
filename);
return GNUNET_SYSERR;
}
- memcpy (&priv,
- buf,
- buf_size);
+ GNUNET_memcpy (&priv,
+ buf,
+ buf_size);
{
struct GNUNET_CRYPTO_EddsaPublicKey pub;
diff --git a/src/util/taler-exchange-secmod-rsa.c b/src/util/taler-exchange-secmod-rsa.c
index c651d78a8..414cb4acc 100644
--- a/src/util/taler-exchange-secmod-rsa.c
+++ b/src/util/taler-exchange-secmod-rsa.c
@@ -394,13 +394,13 @@ generate_response (struct DenominationKey *dk)
&an->secm_sig);
an->secm_pub = TES_smpub;
p = (void *) &an[1];
- memcpy (p,
- buf,
- buf_len);
+ GNUNET_memcpy (p,
+ buf,
+ buf_len);
GNUNET_free (buf);
- memcpy (p + buf_len,
- denom->section,
- nlen);
+ GNUNET_memcpy (p + buf_len,
+ denom->section,
+ nlen);
dk->an = an;
}
@@ -524,9 +524,9 @@ send_signature (struct TES_Client *client,
sr = GNUNET_malloc (tsize);
sr->header.size = htons (tsize);
sr->header.type = htons (TALER_HELPER_RSA_MT_RES_SIGNATURE);
- memcpy (&sr[1],
- buf,
- buf_size);
+ GNUNET_memcpy (&sr[1],
+ buf,
+ buf_size);
GNUNET_free (buf);
ret = TES_transmit (client->csock,
&sr->header);
@@ -1111,9 +1111,9 @@ rsa_client_init (struct TES_Client *client)
NULL != dk;
dk = dk->next)
{
- memcpy (&buf[obs],
- dk->an,
- ntohs (dk->an->header.size));
+ GNUNET_memcpy (&buf[obs],
+ dk->an,
+ ntohs (dk->an->header.size));
GNUNET_assert (obs + ntohs (dk->an->header.size)
> obs);
obs += ntohs (dk->an->header.size);
@@ -1212,18 +1212,18 @@ rsa_update_client_keys (struct TES_Client *client)
.h_rsa = key->h_rsa
};
- memcpy (&buf[obs],
- &pn,
- sizeof (pn));
+ GNUNET_memcpy (&buf[obs],
+ &pn,
+ sizeof (pn));
GNUNET_assert (obs + sizeof (pn)
> obs);
obs += sizeof (pn);
}
else
{
- memcpy (&buf[obs],
- key->an,
- ntohs (key->an->header.size));
+ GNUNET_memcpy (&buf[obs],
+ key->an,
+ ntohs (key->an->header.size));
GNUNET_assert (obs + ntohs (key->an->header.size)
> obs);
obs += ntohs (key->an->header.size);
diff --git a/src/util/test_conversion.c b/src/util/test_conversion.c
new file mode 100644
index 000000000..00cb35e72
--- /dev/null
+++ b/src/util/test_conversion.c
@@ -0,0 +1,149 @@
+/*
+ This file is part of TALER
+ (C) 2023 Taler Systems SA
+
+ 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, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/test_conversion.c
+ * @brief Tests for conversion logic
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include <gnunet/gnunet_json_lib.h>
+
+/**
+ * Return value from main().
+ */
+static int global_ret;
+
+/**
+ * Handle to our helper.
+ */
+static struct TALER_JSON_ExternalConversion *ec;
+
+
+/**
+ * Type of a callback that receives a JSON @a result.
+ *
+ * @param cls closure
+ * @param status_type how did the process die
+ * @apram code termination status code from the process
+ * @param result some JSON result, NULL if we failed to get an JSON output
+ */
+static void
+conv_cb (void *cls,
+ enum GNUNET_OS_ProcessStatusType status_type,
+ unsigned long code,
+ const json_t *result)
+{
+ json_t *expect;
+
+ (void) cls;
+ (void) status_type;
+ ec = NULL;
+ global_ret = 3;
+ if (42 != code)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected return value from helper: %u\n",
+ (unsigned int) code);
+ return;
+ }
+ expect = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("foo",
+ "arg")
+ );
+ if (1 == json_equal (expect,
+ result))
+ {
+ global_ret = 0;
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected JSON result\n");
+ json_dumpf (result,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = 4;
+ }
+ json_decref (expect);
+}
+
+
+/**
+ * Function called on shutdown/CTRL-C.
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+ (void) cls;
+ if (NULL != ec)
+ {
+ GNUNET_break (0);
+ global_ret = 2;
+ TALER_JSON_external_conversion_stop (ec);
+ ec = NULL;
+ }
+}
+
+
+/**
+ * Main test function.
+ *
+ * @param cls NULL
+ */
+static void
+run (void *cls)
+{
+ json_t *input;
+
+ (void) cls;
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+ NULL);
+ input = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("key",
+ "foo")
+ );
+ ec = TALER_JSON_external_conversion_start (input,
+ &conv_cb,
+ NULL,
+ "./test_conversion.sh",
+ "test_conversion.sh",
+ "arg",
+ NULL);
+ json_decref (input);
+ GNUNET_assert (NULL != ec);
+}
+
+
+int
+main (int argc,
+ const char *const argv[])
+{
+ (void) argc;
+ (void) argv;
+ unsetenv ("XDG_DATA_HOME");
+ unsetenv ("XDG_CONFIG_HOME");
+ GNUNET_log_setup ("test-conversion",
+ "WARNING",
+ NULL);
+ GNUNET_OS_init (TALER_project_data_default ());
+ global_ret = 1;
+ GNUNET_SCHEDULER_run (&run,
+ NULL);
+ return global_ret;
+}
diff --git a/src/util/test_conversion.sh b/src/util/test_conversion.sh
new file mode 100755
index 000000000..26e1a36d8
--- /dev/null
+++ b/src/util/test_conversion.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+KEY=$(jq -r .key)
+echo -n "{\"$KEY\":\"$1\"}"
+exit 42
diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c
index 80a247770..80ce9083d 100644
--- a/src/util/test_crypto.c
+++ b/src/util/test_crypto.c
@@ -355,15 +355,24 @@ test_exchange_sigs (void)
struct TALER_MasterPrivateKeyP priv;
struct TALER_MasterPublicKeyP pub;
struct TALER_MasterSignatureP sig;
+ json_t *rest;
GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv);
+ rest = json_array ();
+ GNUNET_assert (NULL != rest);
TALER_exchange_wire_signature_make (pt,
+ NULL,
+ rest,
+ rest,
&priv,
&sig);
GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv,
&pub.eddsa_pub);
if (GNUNET_OK !=
TALER_exchange_wire_signature_check (pt,
+ NULL,
+ rest,
+ rest,
&pub,
&sig))
{
@@ -373,12 +382,28 @@ test_exchange_sigs (void)
if (GNUNET_OK ==
TALER_exchange_wire_signature_check (
"payto://x-taler-bank/localhost/Other",
+ NULL,
+ rest,
+ rest,
&pub,
&sig))
{
GNUNET_break (0);
return 1;
}
+ if (GNUNET_OK ==
+ TALER_exchange_wire_signature_check (
+ pt,
+ "http://example.com/",
+ rest,
+ rest,
+ &pub,
+ &sig))
+ {
+ GNUNET_break (0);
+ return 1;
+ }
+ json_decref (rest);
return 0;
}
diff --git a/src/util/util.c b/src/util/util.c
index 96d791912..da5727487 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -25,6 +25,7 @@
#include "taler_util.h"
#include "taler_attributes.h"
#include <gnunet/gnunet_json_lib.h>
+#include <unistr.h>
const char *
@@ -40,9 +41,9 @@ TALER_b2s (const void *buf,
&hc);
tmp = GNUNET_STRINGS_data_to_string_alloc (&hc,
sizeof (hc));
- memcpy (ret,
- tmp,
- 8);
+ GNUNET_memcpy (ret,
+ tmp,
+ 8);
GNUNET_free (tmp);
ret[8] = '\0';
return ret;
@@ -205,6 +206,164 @@ TALER_denom_fee_check_currency (
/**
+ * Dump character in the low range into @a buf
+ * following RFC 8785.
+ *
+ * @param[in,out] buf buffer to modify
+ * @param val value to dump
+ */
+static void
+lowdump (struct GNUNET_Buffer *buf,
+ unsigned char val)
+{
+ char scratch[7];
+
+ switch (val)
+ {
+ case 0x8:
+ GNUNET_buffer_write (buf,
+ "\\b",
+ 2);
+ break;
+ case 0x9:
+ GNUNET_buffer_write (buf,
+ "\\t",
+ 2);
+ break;
+ case 0xA:
+ GNUNET_buffer_write (buf,
+ "\\n",
+ 2);
+ break;
+ case 0xC:
+ GNUNET_buffer_write (buf,
+ "\\f",
+ 2);
+ break;
+ case 0xD:
+ GNUNET_buffer_write (buf,
+ "\\r",
+ 2);
+ break;
+ default:
+ GNUNET_snprintf (scratch,
+ sizeof (scratch),
+ "\\u%04x",
+ (unsigned int) val);
+ GNUNET_buffer_write (buf,
+ scratch,
+ 6);
+ break;
+ }
+}
+
+
+size_t
+TALER_rfc8785encode (char **inp)
+{
+ struct GNUNET_Buffer buf = { 0 };
+ size_t left = strlen (*inp) + 1;
+ size_t olen;
+ char *in = *inp;
+ const char *pos = in;
+
+ GNUNET_buffer_prealloc (&buf,
+ left + 40);
+ buf.warn_grow = 0; /* disable, + 40 is just a wild guess */
+ while (1)
+ {
+ int mbl = u8_mblen ((unsigned char *) pos,
+ left);
+ unsigned char val;
+
+ if (0 == mbl)
+ break;
+ val = (unsigned char) *pos;
+ if ( (1 == mbl) &&
+ (val <= 0x1F) )
+ {
+ /* Should not happen, as input is produced by
+ * JSON stringification */
+ GNUNET_break (0);
+ lowdump (&buf,
+ val);
+ }
+ else if ( (1 == mbl) && ('\\' == *pos) )
+ {
+ switch (*(pos + 1))
+ {
+ case '\\':
+ mbl = 2;
+ GNUNET_buffer_write (&buf,
+ pos,
+ mbl);
+ break;
+ case 'u':
+ {
+ unsigned int num;
+ uint32_t n32;
+ unsigned char res[8];
+ size_t rlen;
+
+ GNUNET_assert ( (1 ==
+ sscanf (pos + 2,
+ "%4x",
+ &num)) ||
+ (1 ==
+ sscanf (pos + 2,
+ "%4X",
+ &num)) );
+ mbl = 6;
+ n32 = (uint32_t) num;
+ rlen = sizeof (res);
+ u32_to_u8 (&n32,
+ 1,
+ res,
+ &rlen);
+ if ( (1 == rlen) &&
+ (res[0] <= 0x1F) )
+ {
+ lowdump (&buf,
+ res[0]);
+ }
+ else
+ {
+ GNUNET_buffer_write (&buf,
+ (const char *) res,
+ rlen);
+ }
+ }
+ break;
+ default:
+ mbl = 2;
+ GNUNET_buffer_write (&buf,
+ pos,
+ mbl);
+ break;
+ }
+ }
+ else
+ {
+ GNUNET_buffer_write (&buf,
+ pos,
+ mbl);
+ }
+ left -= mbl;
+ pos += mbl;
+ }
+
+ /* 0-terminate buffer */
+ GNUNET_buffer_write (&buf,
+ "",
+ 1);
+ GNUNET_free (in);
+ *inp = GNUNET_buffer_reap (&buf,
+ &olen);
+ return olen;
+}
+
+
+/**
* Hash normalized @a j JSON object or array and
* store the result in @a hc.
*
@@ -221,11 +380,11 @@ TALER_json_hash (const json_t *j,
cstr = json_dumps (j,
JSON_COMPACT | JSON_SORT_KEYS);
GNUNET_assert (NULL != cstr);
- clen = strlen (cstr);
+ clen = TALER_rfc8785encode (&cstr);
GNUNET_CRYPTO_hash (cstr,
clen,
hc);
- free (cstr);
+ GNUNET_free (cstr);
}