summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/Makefile.am14
-rw-r--r--contrib/ci/Containerfile81
-rwxr-xr-xcontrib/ci/ci.sh34
-rw-r--r--contrib/ci/jobs/0-codespell/config.ini6
-rw-r--r--contrib/ci/jobs/0-codespell/dictionary.txt15
-rwxr-xr-xcontrib/ci/jobs/0-codespell/job.sh31
-rwxr-xr-xcontrib/ci/jobs/1-build/build.sh10
-rwxr-xr-xcontrib/ci/jobs/1-build/job.sh9
-rw-r--r--contrib/ci/jobs/2-test/config.ini6
-rwxr-xr-xcontrib/ci/jobs/2-test/job.sh6
-rwxr-xr-xcontrib/ci/jobs/2-test/test.sh51
-rw-r--r--contrib/ci/jobs/3-docs/config.ini6
-rwxr-xr-xcontrib/ci/jobs/3-docs/docs.sh11
-rwxr-xr-xcontrib/ci/jobs/3-docs/job.sh6
-rwxr-xr-xcontrib/ci/jobs/4-deb-package/job.sh23
-rwxr-xr-xcontrib/ci/jobs/4-deb-package/version.sh17
-rw-r--r--contrib/ci/jobs/5-deploy-package/config.ini6
-rwxr-xr-xcontrib/ci/jobs/5-deploy-package/job.sh14
-rw-r--r--contrib/microhttpd.tag32
-rwxr-xr-xcontrib/sync-dbconfig149
-rw-r--r--contrib/taler-exchange.tag118
-rw-r--r--contrib/uncrustify.cfg19
-rwxr-xr-xcontrib/uncrustify.sh14
23 files changed, 676 insertions, 2 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
new file mode 100644
index 0000000..b6eac2f
--- /dev/null
+++ b/contrib/Makefile.am
@@ -0,0 +1,14 @@
+# This file is in the public domain.
+
+SUBDIRS = .
+
+bin_SCRIPTS = \
+ sync-dbconfig
+
+EXTRA_DIST = \
+ $(bin_SCRIPTS) \
+ gnunet.tag \
+ microhttpd.tag \
+ taler-exchange.tag \
+ uncrustify.cfg \
+ uncrustify_precommit
diff --git a/contrib/ci/Containerfile b/contrib/ci/Containerfile
new file mode 100644
index 0000000..96c9b28
--- /dev/null
+++ b/contrib/ci/Containerfile
@@ -0,0 +1,81 @@
+FROM docker.io/library/debian:bookworm
+
+ENV DEBIAN_FRONTEND=noninteractive
+
+RUN apt-get update -yqq && \
+ apt-get install -yqq \
+ git \
+ autoconf \
+ libjansson-dev \
+ libgcrypt-dev \
+ libqrencode-dev \
+ libpq-dev \
+ pkg-config \
+ libtool \
+ recutils \
+ make \
+ python3-pip \
+ python3-sphinx \
+ python3-sphinx-rtd-theme \
+ texinfo \
+ autopoint \
+ curl \
+ wget \
+ libcurl4-gnutls-dev \
+ libsodium-dev \
+ libidn11-dev \
+ zlib1g-dev \
+ libunistring-dev
+
+# Debian packaging tools
+RUN apt-get install -yqq \
+ po-debconf \
+ build-essential \
+ debhelper-compat \
+ devscripts \
+ git-buildpackage
+
+RUN pip3 install --break-system-packages requests click poetry uwsgi htmlark
+
+# Install docs generation utils
+RUN apt-get update -yqq && \
+ apt-get install -yqq \
+ graphviz \
+ doxygen \
+ && rm -rf /var/lib/apt/lists/*
+
+# Install Taler (and friends) packages
+RUN curl -sS https://deb.taler.net/apt-nightly/taler-bookworm-ci.sources \
+ | tee /etc/apt/sources.list.d/taler-bookworm-ci.sources
+
+RUN echo '\
+Package: * \n\
+Pin: origin "deb.taler.net" \n\
+Pin-Priority: 999' > /etc/apt/preferences.d/taler
+
+# FIXME: we need libeufin-bank here for the CI to work!
+RUN cat /etc/apt/preferences.d/taler && \
+ apt-get update -y && \
+ apt-get install -y \
+ libgnunet-dev \
+ libgnunet \
+ libtalerexchange-dev \
+ libtalerexchange \
+ libtalermerchant-dev \
+ libtalermerchant \
+ taler-exchange \
+ taler-merchant \
+ taler-exchange-database \
+ taler-exchange-offline \
+ taler-auditor \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN apt-get update -yqq && \
+ apt-get install -yqq \
+ postgresql \
+ sudo \
+ jq
+
+WORKDIR /workdir
+
+CMD ["bash", "/workdir/ci/ci.sh"]
diff --git a/contrib/ci/ci.sh b/contrib/ci/ci.sh
new file mode 100755
index 0000000..0719015
--- /dev/null
+++ b/contrib/ci/ci.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+set -exvuo pipefail
+
+# Requires podman
+# Fails if not found in PATH
+OCI_RUNTIME=$(which podman)
+REPO_NAME=$(basename "${PWD}")
+JOB_NAME="${1}"
+JOB_ARCH=$((grep CONTAINER_ARCH contrib/ci/jobs/${JOB_NAME}/config.ini | cut -d' ' -f 3) || echo "${2:-amd64}")
+JOB_CONTAINER=$((grep CONTAINER_NAME contrib/ci/jobs/${JOB_NAME}/config.ini | cut -d' ' -f 3) || echo "localhost/${REPO_NAME}:${JOB_ARCH}")
+CONTAINER_BUILD=$((grep CONTAINER_BUILD contrib/ci/jobs/${JOB_NAME}/config.ini | cut -d' ' -f 3) || echo "True")
+
+echo "Image name: ${JOB_CONTAINER}"
+
+if [ "${CONTAINER_BUILD}" = "True" ] ; then
+ "${OCI_RUNTIME}" build \
+ --arch "${JOB_ARCH}" \
+ -t "${JOB_CONTAINER}" \
+ -f contrib/ci/Containerfile .
+fi
+
+"${OCI_RUNTIME}" run \
+ --rm \
+ -ti \
+ --arch "${JOB_ARCH}" \
+ --env CI_COMMIT_REF="$(git rev-parse HEAD)" \
+ --volume "${PWD}":/workdir \
+ --workdir /workdir \
+ "${JOB_CONTAINER}" \
+ contrib/ci/jobs/"${JOB_NAME}"/job.sh
+
+top_dir=$(dirname "${BASH_SOURCE[0]}")
+
+#"${top_dir}"/build.sh
diff --git a/contrib/ci/jobs/0-codespell/config.ini b/contrib/ci/jobs/0-codespell/config.ini
new file mode 100644
index 0000000..bd7d738
--- /dev/null
+++ b/contrib/ci/jobs/0-codespell/config.ini
@@ -0,0 +1,6 @@
+[build]
+HALT_ON_FAILURE = False
+WARN_ON_FAILURE = True
+CONTAINER_BUILD = False
+CONTAINER_NAME = nixery.dev/shell/codespell
+CONTAINER_ARCH = amd64
diff --git a/contrib/ci/jobs/0-codespell/dictionary.txt b/contrib/ci/jobs/0-codespell/dictionary.txt
new file mode 100644
index 0000000..5ad828f
--- /dev/null
+++ b/contrib/ci/jobs/0-codespell/dictionary.txt
@@ -0,0 +1,15 @@
+# List of "words" that codespell should ignore in our sources.
+#
+# Note: The word sensitivity depends on how the to-be-ignored word is
+# spelled in codespell_lib/data/dictionary.txt. F.e. if there is a word
+# 'foo' and you add 'Foo' _here_, codespell will continue to complain
+# about 'Foo'.
+#
+ifset
+bu
+fIDN
+ECT
+complet
+ges
+UE
+Te
diff --git a/contrib/ci/jobs/0-codespell/job.sh b/contrib/ci/jobs/0-codespell/job.sh
new file mode 100755
index 0000000..bb02feb
--- /dev/null
+++ b/contrib/ci/jobs/0-codespell/job.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+set -exuo pipefail
+
+job_dir=$(dirname "${BASH_SOURCE[0]}")
+
+skip=$(cat <<EOF
+ABOUT-NLS
+configure
+config.guess
+configure~
+*/debian/upstream/*
+*/debian/.debhelper/*
+*/doc/prebuilt/*
+*/testing/test_sync_api_home/*
+*build-aux*
+*.cache/*
+*/.git/*
+*/contrib/ci/*
+depcomp
+*libtool*
+ltmain.sh
+*.log
+*/m4/*
+*.m4
+*.rpath
+EOF
+);
+
+echo Current directory: `pwd`
+
+codespell -I "${job_dir}"/dictionary.txt -S ${skip//$'\n'/,}
diff --git a/contrib/ci/jobs/1-build/build.sh b/contrib/ci/jobs/1-build/build.sh
new file mode 100755
index 0000000..7d1b502
--- /dev/null
+++ b/contrib/ci/jobs/1-build/build.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+set -exuo pipefail
+
+./bootstrap
+./configure CFLAGS="-ggdb -O0" \
+ --prefix=/usr \
+ --enable-logging=verbose \
+ --disable-doc
+
+make
diff --git a/contrib/ci/jobs/1-build/job.sh b/contrib/ci/jobs/1-build/job.sh
new file mode 100755
index 0000000..c1fc4e3
--- /dev/null
+++ b/contrib/ci/jobs/1-build/job.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -exuo pipefail
+
+apt-get update -yq
+apt-get upgrade -yq
+
+job_dir=$(dirname "${BASH_SOURCE[0]}")
+
+"${job_dir}"/build.sh
diff --git a/contrib/ci/jobs/2-test/config.ini b/contrib/ci/jobs/2-test/config.ini
new file mode 100644
index 0000000..d62c4d4
--- /dev/null
+++ b/contrib/ci/jobs/2-test/config.ini
@@ -0,0 +1,6 @@
+[build]
+HALT_ON_FAILURE = False
+WARN_ON_FAILURE = True
+CONTAINER_BUILD = True
+CONTAINER_NAME = localhost/sync
+CONTAINER_ARCH = amd64
diff --git a/contrib/ci/jobs/2-test/job.sh b/contrib/ci/jobs/2-test/job.sh
new file mode 100755
index 0000000..bfb24e3
--- /dev/null
+++ b/contrib/ci/jobs/2-test/job.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -exuo pipefail
+
+job_dir=$(dirname "${BASH_SOURCE[0]}")
+
+"${job_dir}"/test.sh
diff --git a/contrib/ci/jobs/2-test/test.sh b/contrib/ci/jobs/2-test/test.sh
new file mode 100755
index 0000000..aebbe01
--- /dev/null
+++ b/contrib/ci/jobs/2-test/test.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+set -evu
+
+apt-get update
+apt-get upgrade -yqq
+
+./bootstrap
+./configure CFLAGS="-ggdb -O0" \
+ --prefix=/usr \
+ --enable-logging=verbose \
+ --disable-doc
+make -j install
+
+sudo -u postgres /usr/lib/postgresql/15/bin/postgres -D /etc/postgresql/15/main -h localhost -p 5432 &
+sleep 10
+sudo -u postgres createuser -p 5432 root
+sudo -u postgres createdb -p 5432 -O root synccheck
+
+check_command()
+{
+ # Set LD_LIBRARY_PATH so tests can find the installed libs
+ LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/taler:/usr/lib:/usr/lib/taler PGPORT=5432 make check
+}
+
+print_logs()
+{
+ for i in src/*/test-suite.log
+ do
+ for FAILURE in $(grep '^FAIL:' ${i} | cut -d' ' -f2)
+ do
+ echo "Printing ${FAILURE}.log"
+ echo "========BEGIN======"
+ cat "$(dirname $i)/${FAILURE}.log"
+ echo "=========END======="
+ echo "End of ${FAILURE}.log"
+ done
+ done
+ for LOGFILE in src/testing/*.log
+ do
+ echo "Printing ${LOGFILE}"
+ echo "========BEGIN======"
+ cat "${LOGFILE}"
+ echo "=========END======="
+ echo "End of ${LOGFILE}"
+ done
+}
+
+if ! check_command ; then
+ print_logs
+ exit 1
+fi
diff --git a/contrib/ci/jobs/3-docs/config.ini b/contrib/ci/jobs/3-docs/config.ini
new file mode 100644
index 0000000..d62c4d4
--- /dev/null
+++ b/contrib/ci/jobs/3-docs/config.ini
@@ -0,0 +1,6 @@
+[build]
+HALT_ON_FAILURE = False
+WARN_ON_FAILURE = True
+CONTAINER_BUILD = True
+CONTAINER_NAME = localhost/sync
+CONTAINER_ARCH = amd64
diff --git a/contrib/ci/jobs/3-docs/docs.sh b/contrib/ci/jobs/3-docs/docs.sh
new file mode 100755
index 0000000..fe2b968
--- /dev/null
+++ b/contrib/ci/jobs/3-docs/docs.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+set -exuo pipefail
+
+./bootstrap
+./configure --enable-only-doc
+
+pushd ./doc/doxygen/
+
+make full
+
+popd
diff --git a/contrib/ci/jobs/3-docs/job.sh b/contrib/ci/jobs/3-docs/job.sh
new file mode 100755
index 0000000..a72bca4
--- /dev/null
+++ b/contrib/ci/jobs/3-docs/job.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -exuo pipefail
+
+job_dir=$(dirname "${BASH_SOURCE[0]}")
+
+"${job_dir}"/docs.sh
diff --git a/contrib/ci/jobs/4-deb-package/job.sh b/contrib/ci/jobs/4-deb-package/job.sh
new file mode 100755
index 0000000..42636ed
--- /dev/null
+++ b/contrib/ci/jobs/4-deb-package/job.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -exuo pipefail
+# This file is in the public domain.
+# Helper script to build the latest DEB packages in the container.
+
+
+unset LD_LIBRARY_PATH
+
+# Install build-time dependencies.
+# Update apt cache first
+apt-get update
+apt-get upgrade -y
+mk-build-deps --install --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
+
+export VERSION="$(./contrib/ci/jobs/4-deb-package/version.sh)"
+echo "Building package version ${VERSION}"
+EMAIL=none gbp dch --ignore-branch --debian-tag="%(version)s" --git-author --new-version="${VERSION}"
+./bootstrap
+dpkg-buildpackage -rfakeroot -b -uc -us
+
+ls -alh ../*.deb
+mkdir -p /artifacts/sync/${CI_COMMIT_REF} # Variable comes from CI environment
+mv ../*.deb /artifacts/sync/${CI_COMMIT_REF}/
diff --git a/contrib/ci/jobs/4-deb-package/version.sh b/contrib/ci/jobs/4-deb-package/version.sh
new file mode 100755
index 0000000..52031b2
--- /dev/null
+++ b/contrib/ci/jobs/4-deb-package/version.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+set -ex
+
+BRANCH=$(git name-rev --name-only HEAD)
+if [ -z "${BRANCH}" ]; then
+ exit 1
+else
+ # "Unshallow" our checkout, but only our current branch, and exclude the submodules.
+ git fetch --no-recurse-submodules --tags --depth=1000 origin "${BRANCH}"
+ RECENT_VERSION_TAG=$(git describe --tags --match 'v*.*.*' --exclude '*-dev*' --always --abbrev=0 HEAD || exit 1)
+ commits="$(git rev-list ${RECENT_VERSION_TAG}..HEAD --count)"
+ if [ "${commits}" = "0" ]; then
+ git describe --tag HEAD | sed -r 's/^v//' || exit 1
+ else
+ echo $(echo ${RECENT_VERSION_TAG} | sed -r 's/^v//')-${commits}-$(git rev-parse --short=8 HEAD)
+ fi
+fi
diff --git a/contrib/ci/jobs/5-deploy-package/config.ini b/contrib/ci/jobs/5-deploy-package/config.ini
new file mode 100644
index 0000000..08c106f
--- /dev/null
+++ b/contrib/ci/jobs/5-deploy-package/config.ini
@@ -0,0 +1,6 @@
+[build]
+HALT_ON_FAILURE = True
+WARN_ON_FAILURE = True
+CONTAINER_BUILD = False
+CONTAINER_NAME = nixery.dev/shell/rsync
+CONTAINER_ARCH = amd64
diff --git a/contrib/ci/jobs/5-deploy-package/job.sh b/contrib/ci/jobs/5-deploy-package/job.sh
new file mode 100755
index 0000000..9ad8f21
--- /dev/null
+++ b/contrib/ci/jobs/5-deploy-package/job.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -exuo pipefail
+
+ARTIFACT_PATH="/artifacts/sync/${CI_COMMIT_REF}/*.deb"
+
+RSYNC_HOST="taler.host.internal"
+RSYNC_PORT=424242
+RSYNC_PATH="incoming_packages/bookworm-taler-ci/"
+RSYNC_DEST="rsync://${RSYNC_HOST}/${RSYNC_PATH}"
+
+
+rsync -vP \
+ --port ${RSYNC_PORT} \
+ ${ARTIFACT_PATH} ${RSYNC_DEST}
diff --git a/contrib/microhttpd.tag b/contrib/microhttpd.tag
index 8fab93d..ade11c6 100644
--- a/contrib/microhttpd.tag
+++ b/contrib/microhttpd.tag
@@ -24,6 +24,18 @@
</member>
<member kind="define">
<type>#define</type>
+ <name>MHD_HTTP_NOT_FOUND</name>
+ <anchorfile>microhttpd.h</anchorfile>
+ <arglist></arglist>
+ </member>
+ <member kind="define">
+ <type>#define</type>
+ <name>MHD_HTTP_CONFLICT</name>
+ <anchorfile>microhttpd.h</anchorfile>
+ <arglist></arglist>
+ </member>
+ <member kind="define">
+ <type>#define</type>
<name>MHD_HTTP_NO_CONTENT</name>
<anchorfile>microhttpd.h</anchorfile>
<arglist></arglist>
@@ -64,11 +76,29 @@
<anchorfile>microhttpd.h</anchorfile>
<arglist></arglist>
</member>
+ <member kind="define">
+ <type>function</type>
+ <name>MHD_run</name>
+ <anchorfile>microhttpd.h</anchorfile>
+ <arglist></arglist>
+ </member>
+ <member kind="define">
+ <type>function</type>
+ <name>MHD_get_connection_values</name>
+ <anchorfile>microhttpd.h</anchorfile>
+ <arglist></arglist>
+ </member>
<member kind="typedef">
<type>int</type>
<name>MHD_AccessHandlerCallback</name>
<anchorfile>microhttpd.h</anchorfile>
- <arglist>)(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls)</arglist>
+ <arglist></arglist>
+ </member>
+ <member kind="typedef">
+ <type>int</type>
+ <name>MHD_RequestCompletedCallback</name>
+ <anchorfile>microhttpd.h</anchorfile>
+ <arglist></arglist>
</member>
</compound>
</tagfile>
diff --git a/contrib/sync-dbconfig b/contrib/sync-dbconfig
new file mode 100755
index 0000000..d0d3a4b
--- /dev/null
+++ b/contrib/sync-dbconfig
@@ -0,0 +1,149 @@
+#!/bin/bash
+# This file is part of GNU 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 Lesser General Public License as published by the Free Software
+# Foundation; either version 2.1, 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along with
+# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+# @author Christian Grothoff
+#
+#
+# Error checking on
+set -eu
+
+RESET_DB=0
+SKIP_DBINIT=0
+DBUSER="sync-httpd"
+CFGFILE="/etc/sync/sync.conf"
+
+# Parse command-line options
+while getopts 'c:hrsu:' OPTION; do
+ case "$OPTION" in
+ c)
+ CFGFILE="$OPTARG"
+ ;;
+ h)
+ echo 'Supported options:'
+ echo " -c FILENAME -- write configuration to FILENAME (default: $CFGFILE)"
+ echo " -r -- reset database (dangerous)"
+ echo " -s -- skip database initialization"
+ echo " -u USER -- sync-httpd to be run by USER (default: $DBUSER)"
+ exit 0
+ ;;
+ r)
+ RESET_DB="1"
+ ;;
+ s)
+ SKIP_DBINIT="1"
+ ;;
+ u)
+ DBUSER="$OPTARG"
+ ;;
+ ?)
+ exit_fail "Unrecognized command line option"
+ ;;
+ esac
+done
+
+if ! id postgres > /dev/null
+then
+ echo "Could not find 'postgres' user. Please install Postgresql first"
+ exit 1
+fi
+
+if [ "$(id -u)" -ne 0 ]
+then
+ echo "This script must be run as root"
+ exit 1
+fi
+
+if [ 0 = "$SKIP_DBINIT" ]
+then
+ if ! sync-dbinit -v 2> /dev/null
+ then
+ echo "Required 'sync-dbinit' not found. Please fix your installation."
+ exit 1
+ fi
+ DBINIT=$(which sync-dbinit)
+fi
+
+if ! id "$DBUSER" > /dev/null
+then
+ echo "Could not find '$DBUSER' user. Please set it up first"
+ exit 1
+fi
+
+echo "Setting up database user $DBUSER." 1>&2
+
+if ! sudo -i -u postgres createuser "$DBUSER" 2> /dev/null
+then
+ echo "Database user '$DBUSER' already existed. Continuing anyway." 1>&2
+fi
+
+DBPATH=$(sync-config \
+ -c "$CFGFILE" \
+ -s syncdb-postgres \
+ -o CONFIG)
+
+if ! echo "$DBPATH" | grep "postgres://" > /dev/null
+then
+ echo "Invalid database configuration value '$DBPATH'." 1>&2
+ exit 1
+fi
+
+DBNAME=$(echo "$DBPATH" \
+ | sed \
+ -e "s/postgres:\/\/.*\///" \
+ -e "s/?.*//")
+
+if sudo -i -u postgres psql "$DBNAME" < /dev/null 2> /dev/null
+then
+ if [ 1 = "$RESET_DB" ]
+ then
+ echo "Deleting existing database $DBNAME." 1>&2
+ if ! sudo -i -u postgres dropdb "$DBNAME"
+ then
+ echo "Failed to delete existing database '$DBNAME'"
+ exit 1
+ fi
+ DO_CREATE=1
+ else
+ echo "Database '$DBNAME' already exists, continuing anyway."
+ DO_CREATE=0
+ fi
+else
+ DO_CREATE=1
+fi
+
+if [1 = "$DO_CREATE" ]
+then
+ echo "Creating database $DBNAME." 1>&2
+
+ if ! sudo -i -u postgres createdb -O "$DBUSER" "$DBNAME"
+ then
+ echo "Failed to create database '$DBNAME'"
+ exit 1
+ fi
+fi
+
+if [ 0 = "$SKIP_DBINIT" ]
+then
+ echo "Initializing database $DBNAME." 1>&2
+ if ! sudo -u "$DBUSER" "$DBINIT" -c "$CFGFILE"
+ then
+ echo "Failed to initialize database schema"
+ exit 1
+ fi
+fi
+
+echo "Database configuration finished." 1>&2
+
+exit 0
diff --git a/contrib/taler-exchange.tag b/contrib/taler-exchange.tag
new file mode 100644
index 0000000..11dcbb5
--- /dev/null
+++ b/contrib/taler-exchange.tag
@@ -0,0 +1,118 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<tagfile doxygen_version="1.9.4">
+ <compound kind="file">
+ <name>taler_error_codes.h</name>
+ <path>/research/taler/exchange/src/include/</path>
+ <filename>d5/dcb/taler__error__codes_8h.html</filename>
+ <member kind="enumeration">
+ <type></type>
+ <name>TALER_ErrorCode</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49a</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_ACCOUNT_UNKNOWN</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aaeffc5c26407ed73d7638a5588de7c1dd</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_BAD_IF_NONE_MATCH</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aab4c0f2469a013643db71e9d1093a17f0</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_BAD_IF_MATCH</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aa5deccc6c9d196e9a5d15d2b9e8a462ab</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_BAD_SYNC_SIGNATURE</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aa6c88e2f7b362aa75422e188c205501d3</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_INVALID_SIGNATURE</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aa0c79eda43c2de90e28521b466bfeb958</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aaa811843c4362e3b91ff16563a7f11a9f</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_EXCESSIVE_CONTENT_LENGTH</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aac67bcfb7de5cd35d554280caaec494f9</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_OUT_OF_MEMORY_ON_CONTENT_LENGTH</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aa8dced986853a382e1fe1478cd4115d57</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_INVALID_UPLOAD</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aa4ad086f449ddbabebd4c09af46054728</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aacdf25dccb3b6e11b8705f1954e881aac</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_PAYMENT_CREATE_BACKEND_ERROR</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aabbb8e55ff86dbe38c25747fadb64e2a6</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_PREVIOUS_BACKUP_UNKNOWN</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aaeb094aed793a576fdf76fdbca7643686</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_MISSING_CONTENT_LENGTH</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aad04abf794980705173d77fd28435d08a</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_GENERIC_BACKEND_ERROR</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aab534c9117dccea53d74e87ace8e4fea1</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="enumvalue">
+ <name>TALER_EC_SYNC_GENERIC_BACKEND_TIMEOUT</name>
+ <anchorfile>d5/dcb/taler__error__codes_8h.html</anchorfile>
+ <anchor>a05ff23648cb502e6128a53a5aef8d49aa264068a45ffad5779c0de179fdf79658</anchor>
+ <arglist></arglist>
+ </member>
+ </compound>
+ <compound kind="file">
+ <name>taler_mhd_lib.h</name>
+ <path>/research/taler/exchange/src/include/</path>
+ <filename>df/d6d/taler__mhd__lib_8h.html</filename>
+ <includes id="d5/dcb/taler__error__codes_8h" name="taler_error_codes.h" local="yes" imported="no">taler_error_codes.h</includes>
+ <member kind="define">
+ <type>#define</type>
+ <name>TALER_SIGNATURE_SYNC_BACKUP_UPLOAD</name>
+ <anchorfile>dc/d61/taler__signatures_8h.html</anchorfile>
+ <anchor>af15ac86c81f8c3993b56a59a4ccdaa1a</anchor>
+ <arglist></arglist>
+ </member>
+ </compound>
+</tagfile> \ No newline at end of file
diff --git a/contrib/uncrustify.cfg b/contrib/uncrustify.cfg
index f56c8e7..af2d8e6 100644
--- a/contrib/uncrustify.cfg
+++ b/contrib/uncrustify.cfg
@@ -28,7 +28,7 @@ ls_code_width=true
pos_arith=lead
# Fully parenthesize boolean exprs
-mod_full_paren_if_bool=true
+mod_full_paren_if_bool=false
# Braces should be on their own line
nl_fdef_brace=add
@@ -49,8 +49,12 @@ nl_assign_brace=remove
# No extra newlines that cause noisy diffs
nl_start_of_file=remove
+nl_after_func_proto = 2
+nl_after_func_body = 3
# If there's no new line, it's not a text file!
nl_end_of_file=add
+nl_max_blank_in_func = 3
+nl_max = 3
sp_inside_paren = remove
@@ -69,6 +73,7 @@ sp_between_ptr_star = remove
sp_before_sparen = add
sp_inside_fparen = remove
+sp_inside_sparen = remove
# add space before function call and decl: "foo (x)"
sp_func_call_paren = add
@@ -76,3 +81,15 @@ sp_func_proto_paren = add
sp_func_proto_paren_empty = add
sp_func_def_paren = add
sp_func_def_paren_empty = add
+
+# We'd want it for "if ( (foo) || (bar) )", but not for "if (m())",
+# so as uncrustify doesn't give exactly what we want => ignore
+sp_paren_paren = ignore
+sp_inside_paren = remove
+sp_bool = force
+
+nl_func_type_name = force
+#nl_branch_else = add
+nl_else_brace = add
+nl_elseif_brace = add
+nl_for_brace = add
diff --git a/contrib/uncrustify.sh b/contrib/uncrustify.sh
new file mode 100755
index 0000000..e8e05d3
--- /dev/null
+++ b/contrib/uncrustify.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+set -eu
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+if ! uncrustify --version >/dev/null; then
+ echo "you need to install uncrustify for indentation"
+ exit 1
+fi
+
+find "$DIR/../src" \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) \
+ -exec uncrustify -c "$DIR/uncrustify.cfg" --replace --no-backup {} + \
+ || true