taler-mailbox

Service for asynchronous wallet-to-wallet payment messages
Log | Files | Refs | Submodules | README | LICENSE

commit bc4c3fafc16b1db0226943351dac7f144c205b8a
parent 29b8ceb193d4ffacc2a262228536018494ccd6b0
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Sun, 27 Apr 2025 00:41:35 +0200

fix libtool vs package version; vendor base32

Diffstat:
MMakefile.in | 6+++---
Mbootstrap | 8+++++---
Mcmd/mailbox-server/main.go | 2++
Mcmd/mailbox-server/main_test.go | 16++++++++--------
Mconfigure | 16++++++++++++++++
Mgo.mod | 4----
Mgo.sum | 8--------
Ainternal/util/helper.go | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpkg/rest/mailbox.go | 21+++++++++++++--------
9 files changed, 174 insertions(+), 34 deletions(-)

diff --git a/Makefile.in b/Makefile.in @@ -1,15 +1,15 @@ all: server #cli -VERSION="1:0:0" TALER_MAILBOX_HOME=${datadir}/taler-mailbox +LT_VERSION="1:0:0" server: - ${GO} build -ldflags "-X main.version=${VERSION} -X main.mailboxdatahome=${TALER_MAILBOX_HOME}" -o taler-mailbox ./cmd/mailbox-server + ${GO} build -ldflags "-X main.ltversion=${LT_VERSION} -X main.version=${VERSION} -X main.mailboxdatahome=${TALER_MAILBOX_HOME}" -o taler-mailbox ./cmd/mailbox-server #cli: # go build ./cmd/mailbox-cli -install: server gana +install: server mkdir -p ${DESTDIR}${bindir} mkdir -p ${DESTDIR}${TALER_MAILBOX_HOME} install ./taler-mailbox ${DESTDIR}${bindir} diff --git a/bootstrap b/bootstrap @@ -12,6 +12,8 @@ if ! existence go; then exit 1 fi -mkdir -p internal/gana -git submodule update --init --recursive -git submodule sync --recursive +if [ -d ".git" ]; then + mkdir -p internal/gana + git submodule update --init --recursive + git submodule sync --recursive +fi diff --git a/cmd/mailbox-server/main.go b/cmd/mailbox-server/main.go @@ -34,6 +34,7 @@ import ( var ( m mailbox.Mailbox + ltversion string version string mailboxdatahome string verbose bool @@ -85,6 +86,7 @@ func main() { merchToken := iniCfg.Section("mailbox").Key("merchant_token").MustString("secretAccessToken") merch := merchant.NewMerchant(merchURL, merchToken) m.Initialize(mailbox.MailboxConfig{ + LibtoolVersion: ltversion, Version: version, Datahome: mailboxdatahome, Db: db, diff --git a/cmd/mailbox-server/main_test.go b/cmd/mailbox-server/main_test.go @@ -14,12 +14,12 @@ import ( "strings" "testing" - gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util" "github.com/schanzen/taler-go/pkg/merchant" - "github.com/schanzen/taler-go/pkg/util" + talerutil "github.com/schanzen/taler-go/pkg/util" "gopkg.in/ini.v1" "gorm.io/driver/sqlite" "taler.net/taler-mailbox/internal/gana" + "taler.net/taler-mailbox/internal/util" "taler.net/taler-mailbox/pkg/rest" ) @@ -115,7 +115,7 @@ func TestMain(m *testing.M) { testWalletAlice, testWalletAlicePriv, _ = ed25519.GenerateKey(nil) h := sha512.New() h.Write(testWalletAlice) - testWalletAliceString = gnunetutil.EncodeBinaryToString(h.Sum(nil)) + testWalletAliceString = util.Base32CrockfordEncode(h.Sum(nil)) a.Merchant = merchant.NewMerchant(merchServer.URL, "") @@ -156,7 +156,7 @@ func TestPostMessagePaid(t *testing.T) { testMessage := make([]byte, 256) // Make paid - fee, err := util.ParseAmount("KUDOS:1") + fee, err := talerutil.ParseAmount("KUDOS:1") if err != nil { t.Errorf("%v", err) } @@ -179,7 +179,7 @@ func TestPostMessagePaid(t *testing.T) { if body != "" { t.Errorf("Expected empty response, Got %s", body) } - a.MessageFee, _ = util.ParseAmount("KUDOS:0") + a.MessageFee, _ = talerutil.ParseAmount("KUDOS:0") } func TestPostThenDeleteMessage(t *testing.T) { @@ -234,12 +234,12 @@ func TestPostThenDeleteMessage(t *testing.T) { if !ed25519.Verify(testWalletAlice, signed_msg[0:], sig) { t.Errorf("Signature invalid!") } - deletionReq.WalletSig = gnunetutil.EncodeBinaryToString(sig) + deletionReq.WalletSig = util.Base32CrockfordEncode(sig) deletionReq.Count = int(a.MessageResponseLimit) - deletionReq.Checksum = gnunetutil.EncodeBinaryToString(checksum) + deletionReq.Checksum = util.Base32CrockfordEncode(checksum) reqJson, _ := json.Marshal(deletionReq) - hAddress := gnunetutil.EncodeBinaryToString(testWalletAlice) + hAddress := util.Base32CrockfordEncode(testWalletAlice) req, _ = http.NewRequest("DELETE", "/"+hAddress, bytes.NewBuffer(reqJson)) req.Header.Add("If-Match", etag) response = executeRequest(req) diff --git a/configure b/configure @@ -12,6 +12,21 @@ standard_flags="ARFLAGS BISONFLAGS CFLAGS CXXFLAGS CPPFLAGS FLEXFLAGS INSTALLFLA standard_vars="INSTALL_DATA INSTALL_PROGRAM INSTALL_SCRIPT" generated_comment="# This file was generated by configure. DO NOT edit it directly." +if [ -d ".git" ]; then + detected_version=`git describe --tags` + detected_version=${detected_version:1} +else + dirnm=${PWD##*/} + dirnm=${dirnm:-/} + detected_version=${dirnm#*-} + if [ "$detected_version" = "$dirnm" ]; then + detected_version="unknown" + fi +fi + + +echo "Configuring ${pkg_name}-${detected_version}" + # Save arguments cat > config.status <<EOF #!/bin/sh @@ -143,6 +158,7 @@ cat > Makefile <<EOF $generated_comment SHELL = /bin/sh VPATH = ${var_srcdir} +VERSION = ${detected_version} srcdir = ${var_srcdir} prefix = ${var_prefix:-/usr/local} diff --git a/go.mod b/go.mod @@ -3,7 +3,6 @@ module taler.net/taler-mailbox go 1.19 require ( - git.gnunet.org/gnunet-go.git v0.1.28-0.20220717050634-369422be2512 github.com/gorilla/mux v1.8.1 github.com/schanzen/taler-go v1.1.0 gopkg.in/ini.v1 v1.67.0 @@ -14,9 +13,6 @@ require ( ) require ( - filippo.io/edwards25519 v1.1.0 // indirect - github.com/bfix/gospel v1.2.31 // indirect - github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect diff --git a/go.sum b/go.sum @@ -1,13 +1,5 @@ -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -git.gnunet.org/gnunet-go.git v0.1.28-0.20220717050634-369422be2512 h1:RHHLRqEzdblfKOx/isc6E/0/6bJfWLgwWVAXNX5dI7A= -git.gnunet.org/gnunet-go.git v0.1.28-0.20220717050634-369422be2512/go.mod h1:dfSzJbX7Hc7tywZT498/WgTEzoXWZnuvKH6JSFjbyME= -github.com/bfix/gospel v1.2.31 h1:14ymN2Fo7aTYiJ0HEhL0jMg+ofe97HwTHvkntj0DutE= -github.com/bfix/gospel v1.2.31/go.mod h1:s1zIBLFzCWoz2khLuNgDMtzzXtReGmyWVyGTO7ELkHg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= diff --git a/internal/util/helper.go b/internal/util/helper.go @@ -0,0 +1,127 @@ +// This file is part of taldir, the Taler Directory implementation. +// Copyright (C) 2025 Martin Schanzenbach +// +// Taldir is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// Taldir is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package util + +import ( + "errors" + "strings" +) + +//------------------------------------------------------------------------ +// Base32 conversion between binary data and string representation +//------------------------------------------------------------------------ +// +// A binary array of size m is viewed as a consecutive stream of bits +// from left to right. Bytes are ordered with ascending address, while +// bits (in a byte) are ordered MSB to LSB. + +// For encoding the stream is partitioned into 5-bit chunks; the last chunk +// is right-padded with 0's if 8*m is not divisible by 5. Each chunk (value +// between 0 and 31) is encoded into a character; the mapping for encoding +// is the same as in [https://www.crockford.com/wrmg/base32.html]. +// +// For decoding each character is converted to a 5-bit chunk based on the +// encoder mapping (with one addition: the character 'U' maps to the value +// 27). The chunks are concatenated to produce the bit stream to be stored +// in the output array. + +// character set used for encoding/decoding +const xlate = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" + +var ( + // ErrInvalidEncoding signals an invalid encoding + ErrInvalidEncoding = errors.New("invalid encoding") + // ErrBufferTooSmall signalsa too small buffer for decoding + ErrBufferTooSmall = errors.New("buffer to small") +) + +// EncodeBinaryToString encodes a byte array into a string. +func Base32CrockfordEncode(data []byte) string { + size, pos, bits, n := len(data), 0, 0, 0 + out := "" + for { + if n < 5 { + if pos < size { + bits = (bits << 8) | (int(data[pos]) & 0xFF) + pos++ + n += 8 + } else if n > 0 { + bits <<= uint(5 - n) + n = 5 + } else { + break + } + } + out += string(xlate[(bits>>uint(n-5))&0x1F]) + n -= 5 + } + return out +} + +// Base32CrockfordDecode decodes a string into a byte array. +// The function expects the size of the output buffer to be sepcified as an +// argument ('num'); the function returns an error if the buffer is overrun +// or if an invalid character is found in the encoded string. If the decoded +// bit stream is smaller than the output buffer, it is padded with 0's. +func Base32CrockfordDecode(s string, num int) ([]byte, error) { + size := len(s) + out := make([]byte, num) + rpos, wpos, n, bits := 0, 0, 0, 0 + for { + if n < 8 { + if rpos < size { + c := rune(s[rpos]) + rpos++ + v := strings.IndexRune(xlate, c) + if v == -1 { + switch c { + case 'O': + v = 0 + case 'I', 'L': + v = 1 + case 'U': + v = 27 + default: + return nil, ErrInvalidEncoding + } + } + bits = (bits << 5) | (v & 0x1F) + n += 5 + } else { + if wpos < num { + out[wpos] = byte(bits & ((1 << uint(n+1)) - 1)) + wpos++ + for i := wpos; i < num; i++ { + out[i] = 0 + } + } + break + } + } else { + if wpos < num { + out[wpos] = byte((bits >> uint(n-8)) & 0xFF) + wpos++ + n -= 8 + } else { + return nil, ErrBufferTooSmall + } + } + } + return out, nil +} diff --git a/pkg/rest/mailbox.go b/pkg/rest/mailbox.go @@ -35,7 +35,6 @@ import ( "strings" "time" - gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util" "github.com/gorilla/mux" "github.com/schanzen/taler-go/pkg/merchant" tos "github.com/schanzen/taler-go/pkg/rest" @@ -44,11 +43,17 @@ import ( "gorm.io/gorm" "gorm.io/gorm/logger" "taler.net/taler-mailbox/internal/gana" + "taler.net/taler-mailbox/internal/util" ) type TalerMailboxBoxySize int type MailboxConfig struct { + // libtool-style representation of the Mailbox protocol version, see + // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning + // The format is "current:revision:age". + LibtoolVersion string + // Version Version string @@ -96,7 +101,7 @@ type Mailbox struct { // VersionResponse is the JSON response of the /config endpoint type VersionResponse struct { - // libtool-style representation of the Merchant protocol version, see + // libtool-style representation of the Mailbox protocol version, see // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning // The format is "current:revision:age". Version string `json:"version"` @@ -168,7 +173,7 @@ func (m *Mailbox) configResponse(w http.ResponseWriter, r *http.Request) { } cfg := VersionResponse{ - Version: m.Cfg.Version, + Version: m.Cfg.LibtoolVersion, Name: "taler-mailbox", MessageBodyBytes: m.MessageBodyBytes, MessageResponseLimit: m.MessageResponseLimit, @@ -239,7 +244,7 @@ func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) { // Check if order exists and was paid already. h := sha256.New() h.Sum(body) - orderId := gnunetutil.EncodeBinaryToString(h.Sum(nil)) + orderId := util.Base32CrockfordEncode(h.Sum(nil)) httpStatus, paymentStatus, payto, paytoErr := m.Merchant.IsOrderPaid(orderId) if paytoErr != nil { fmt.Println(paytoErr) @@ -317,18 +322,18 @@ func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request) http.Error(w, "If-Match contains malformed etag number", 400) return } - pkey, err := gnunetutil.DecodeStringToBinary(vars["mailbox"], 32) + pkey, err := util.Base32CrockfordDecode(vars["mailbox"], 32) if err != nil { w.WriteHeader(http.StatusBadRequest) return } - checksum, err := gnunetutil.DecodeStringToBinary(msg.Checksum, 64) + checksum, err := util.Base32CrockfordDecode(msg.Checksum, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) return } pk := ed25519.PublicKey(pkey) - sig, err := gnunetutil.DecodeStringToBinary(msg.WalletSig, 64) + sig, err := util.Base32CrockfordDecode(msg.WalletSig, 64) if nil != err { w.WriteHeader(http.StatusBadRequest) return @@ -345,7 +350,7 @@ func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request) } h := sha512.New() h.Write(pkey) - h_mailbox := gnunetutil.EncodeBinaryToString(h.Sum(nil)) + h_mailbox := util.Base32CrockfordEncode(h.Sum(nil)) err = m.Db.Where("h_mailbox = ? AND id >= ?", h_mailbox, etag_expected).Limit(msg.Count).Find(&entries).Error if err != nil { w.WriteHeader(http.StatusInternalServerError)