commit 0f39be86e2f1fba35263ffb0381b06b82f518b09
parent 1d34b9880ad327f9c97d644af8dc17aade3c2c71
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Sun, 15 Feb 2026 11:16:19 +0100
Implement untested GORM replacement for versioned database
Diffstat:
5 files changed, 529 insertions(+), 130 deletions(-)
diff --git a/cmd/mailbox-server/main.go b/cmd/mailbox-server/main.go
@@ -19,15 +19,17 @@
package main
import (
+ "database/sql"
"flag"
"fmt"
- "gopkg.in/ini.v1"
- "gorm.io/driver/postgres"
"log"
"net/http"
"os"
"path"
+ "gopkg.in/ini.v1"
+
+ _ "github.com/lib/pq"
"github.com/schanzen/taler-go/pkg/merchant"
"rsc.io/getopt"
mailbox "taler.net/taler-mailbox/pkg/rest"
@@ -87,13 +89,13 @@ func main() {
log.Printf("Failed to read config: %v", err)
os.Exit(1)
}
- psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
- iniCfg.Section("mailbox-pq").Key("host").MustString("localhost"),
- iniCfg.Section("mailbox-pq").Key("port").MustInt64(5432),
- iniCfg.Section("mailbox-pq").Key("user").MustString("taler-mailbox"),
- iniCfg.Section("mailbox-pq").Key("password").MustString("secret"),
- iniCfg.Section("mailbox-pq").Key("db_name").MustString("taler-mailbox"))
- db := postgres.Open(psqlconn)
+ psqlconn := iniCfg.Section("taldir-pq").Key("connection_string").MustString("postgres:///taler-directory")
+
+ db, err := sql.Open("postgres", psqlconn)
+ if err != nil {
+ log.Panic(err)
+ }
+ defer db.Close()
merchURL := iniCfg.Section("mailbox").Key("merchant_baseurl_private").MustString("http://merchant.mailbox/instances/myInstance")
merchToken := iniCfg.Section("mailbox").Key("merchant_token").MustString("secretAccessToken")
merch := merchant.NewMerchant(merchURL, merchToken)
diff --git a/go.mod b/go.mod
@@ -19,6 +19,7 @@ require (
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
+ github.com/lib/pq v1.11.2 // indirect
github.com/mattn/go-sqlite3 v1.14.28 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect
diff --git a/go.sum b/go.sum
@@ -17,6 +17,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
+github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
diff --git a/pkg/rest/db.go b/pkg/rest/db.go
@@ -0,0 +1,387 @@
+// This file is part of taler-mailbox, the Taler Mailbox implementation.
+// Copyright (C) 2026 Martin Schanzenbach
+//
+// taler-mailbox 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.
+//
+// taler-mailbox 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 mailbox
+
+import (
+ "context"
+ "database/sql"
+ "errors"
+ "fmt"
+ "time"
+)
+
+type MailboxMetadata struct {
+ // ORM
+ Serial int64 `json:"-"`
+
+ // ORM helper hash of signing key
+ HashedSigningKey string `json:"-"`
+
+ // The mailbox signing key.
+ // Note that $H_MAILBOX == H(singingKey).
+ // Note also how this key cannot be updated
+ // as it identifies the mailbox.
+ SigningKey string `json:"signing_key"`
+
+ // Type of key.
+ // Optional, as currently only
+ // EdDSA keys are supported.
+ SigningKeyType string `json:"signing_key_type"`
+
+ // The mailbox encryption key.
+ // This is an HPKE public key
+ // in the X25519 format for use
+ // in a X25519-DHKEM (RFC 9180).
+ // Base32 crockford-encoded.
+ EncryptionKey string `json:"encryption_key"`
+
+ // Type of key.
+ // Optional, as currently only
+ // X25519 keys are supported.
+ EncryptionKeyType string `json:"encryption_key_type"`
+
+ // Expiration of this mapping (UNIX Epoch seconds).
+ Expiration int64 `json:"expiration" gorm:"embedded;embeddedPrefix:expiration_"`
+
+ // Keyoxide claim proof
+ KoxClaimProof string `json:"kox_claim_proof,omitempty"`
+}
+
+type PendingMailboxRegistration struct {
+ // ORM
+ Serial int64 `json:"-"`
+
+ // Hash of the inbox for this entry
+ HashedSigningKey string // Requested registration duration
+
+ // Registration duration (in Seconds)
+ Duration int64
+
+ // The order ID associated with this registration
+ OrderID string `json:"-"`
+}
+
+type InboxEntry struct {
+ // ORM
+ Serial int64 `json:"-"`
+
+ // Encrypted message. Must be exactly 256-32 bytes long.
+ Body []byte
+
+ // Hash of the inbox for this entry
+ HashedSigningKey string
+}
+
+
+func InsertInboxEntryIntoDatabase(db *sql.DB, e *InboxEntry) error {
+ query := `INSERT INTO taler_mailbox.inbox_entries
+ VALUES (DEFAULT, $1, $2);`
+ rows, err := db.Query(query, e.HashedSigningKey, e.Body)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
+}
+
+
+func InsertPendingRegistrationIntoDatabase(db *sql.DB, pr *PendingMailboxRegistration) error {
+ query := `INSERT INTO taler_mailbox.pending_mailbox_registrations
+ VALUES (DEFAULT, $1, $2, $3);`
+ rows, err := db.Query(query, pr.HashedSigningKey, pr.Duration, pr.OrderID)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
+}
+
+
+func InsertMailboxRegistrationIntoDatabase(db *sql.DB, mb *MailboxMetadata) error {
+ query := `INSERT INTO taler_mailbox.mailbox_metadata
+ VALUES (DEFAULT, $1, $2, $3, $4, $5, $6, $7);`
+ rows, err := db.Query(query, mb.HashedSigningKey, mb.SigningKey, mb.SigningKeyType, mb.EncryptionKey, mb.EncryptionKeyType, mb.Expiration, mb.KoxClaimProof)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
+}
+
+func UpdatePendingRegistrationOrderIdInDatabase(db *sql.DB, pr *PendingMailboxRegistration) error {
+ query := `UPDATE taler_mailbox.pending_mailbox_registrations
+ SET
+ "order_id" = $2
+ WHERE "hashed_signing_key" = $1;`
+ rows, err := db.Query(query, pr.HashedSigningKey, pr.OrderID)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
+}
+
+
+func UpdateMailboxExpirationInDatabase(db *sql.DB, mb *MailboxMetadata) error {
+ query := `UPDATE taler_mailbox.mailbox_metadata
+ SET
+ "expiration" = $2
+ WHERE "hashed_signing_key" = $1;`
+ rows, err := db.Query(query, mb.HashedSigningKey, mb.Expiration)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
+}
+
+func GetPendingRegistrationFromDatabaseBySigningKey(db *sql.DB, pr *PendingMailboxRegistration, hashedKey string) error {
+ query := `SELECT
+ "serial",
+ "hashed_signing_key",
+ "duration",
+ "order_id"
+ FROM taler_mailbox.pending_mailbox_registrations
+ WHERE
+ "hashed_signing_key"=$1
+ LIMIT 1
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hashedKey)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ // Iterate over first
+ if !rows.Next() {
+ fmt.Printf("error val %v\n", rows.Err())
+ return errors.New("Mailbox metadata does not exist")
+ }
+ return rows.Scan(
+ &pr.Serial,
+ &pr.HashedSigningKey,
+ &pr.Duration,
+ &pr.OrderID,
+ )
+}
+
+
+func GetMailboxMetadataFromDatabaseBySigningKey(db *sql.DB, mb *MailboxMetadata, hashedKey string) error {
+ query := `SELECT
+ "serial",
+ "hashed_signing_key",
+ "signing_key"
+ "signing_key_type"
+ "encryption_key"
+ "encryption_key_type"
+ "expiration"
+ FROM taler_mailbox.mailbox_metadata
+ WHERE
+ "hashed_signing_key"=$1
+ LIMIT 1
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hashedKey)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ // Iterate over first
+ if !rows.Next() {
+ fmt.Printf("error val %v\n", rows.Err())
+ return errors.New("Mailbox metadata does not exist")
+ }
+ return rows.Scan(
+ &mb.Serial,
+ &mb.HashedSigningKey,
+ &mb.SigningKey,
+ &mb.SigningKeyType,
+ &mb.EncryptionKey,
+ &mb.EncryptionKeyType,
+ &mb.Expiration,
+ )
+}
+
+func GetInboxEntryFromDatabaseBySigningKeyAndBody(db *sql.DB, e *InboxEntry, hashedKey string, body []byte) error {
+ query := `SELECT
+ "serial",
+ "hashed_signing_key",
+ "body"
+ FROM taler_mailbox.inbox_entries
+ WHERE
+ "hashed_signing_key"=$1 AND
+ "body"=$2
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hashedKey, body)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ // Iterate over first
+ if !rows.Next() {
+ fmt.Printf("error val %v\n", rows.Err())
+ return errors.New("Inbox entry does not exist")
+ }
+ return rows.Scan(
+ &e.Serial,
+ &e.HashedSigningKey,
+ &e.Body,
+ )
+}
+
+func GetInboxEntryFromDatabaseBySerial(db *sql.DB, e *InboxEntry, hashedKey string, serial int64) error {
+ query := `SELECT
+ "serial",
+ "hashed_signing_key",
+ "body"
+ FROM taler_mailbox.inbox_entries
+ WHERE
+ "serial"=$1 AND
+ "hashed_signing_key"=$2
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, serial, hashedKey)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ // Iterate over first
+ if !rows.Next() {
+ fmt.Printf("error val %v\n", rows.Err())
+ return errors.New("Inbox entry does not exist")
+ }
+ return rows.Scan(
+ &e.Serial,
+ &e.HashedSigningKey,
+ &e.Body,
+ )
+}
+
+func DeletePendingRegistrationFromDatabase(db *sql.DB, pr *PendingMailboxRegistration) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_mailbox.pending_mailbox_registrations
+ WHERE
+ "serial" = $1
+ ;`
+ // Execute Query
+ result, err := conn.ExecContext(ctx, query, pr.Serial)
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+
+// DeleteInboxEntryFromSerialFromDatabase Deletes all entries starting from given serial
+func DeleteInboxEntryFromSerialFromDatabase(db *sql.DB, e *InboxEntry) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_mailbox.inbox_entries
+ WHERE
+ "hashed_signing_key" = $1 AND
+ "serial" >= $2
+ ;`
+ // Execute Query
+ result, err := conn.ExecContext(ctx, query, e.HashedSigningKey, e.Serial)
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+
+// DeleteStaleRegstrationsFromDatabase purges stale registrations
+func DeleteStaleRegistrationsFromDatabase(db *sql.DB, registrationExpiration time.Time) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_mailbox.mailbox_metadata
+ WHERE
+ "expiration" < $1
+ ;`
+ // Execute Query
+ result, err := conn.ExecContext(ctx, query, registrationExpiration.Unix())
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+// DeleteStalePendingRegstrationsFromDatabase purges stale registrations
+func DeleteStalePendingRegistrationsFromDatabase(db *sql.DB, registrationExpiration time.Time) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_mailbox.pending_mailbox_registrations
+ WHERE
+ "created_at" < $1
+ ;`
+ // Execute Query
+ result, err := conn.ExecContext(ctx, query, registrationExpiration.Unix())
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+
diff --git a/pkg/rest/mailbox.go b/pkg/rest/mailbox.go
@@ -22,6 +22,7 @@ package mailbox
import (
"crypto/ed25519"
"crypto/sha512"
+ "database/sql"
"encoding/binary"
"encoding/json"
"errors"
@@ -39,8 +40,6 @@ import (
tos "github.com/schanzen/taler-go/pkg/rest"
talerutil "github.com/schanzen/taler-go/pkg/util"
"gopkg.in/ini.v1"
- "gorm.io/gorm"
- "gorm.io/gorm/logger"
"taler.net/taler-mailbox/internal/gana"
"taler.net/taler-mailbox/internal/util"
)
@@ -76,8 +75,8 @@ type MailboxConfig struct {
// Configuration
Ini *ini.File
- // DB connection
- DB gorm.Dialector
+ // The database connection to use
+ DB *sql.DB
// Merchant connection
Merchant merchant.Merchant
@@ -92,8 +91,8 @@ type Mailbox struct {
// The main router
Router *mux.Router
- // The main DB handle
- DB *gorm.DB
+ // The database connection to use
+ DB *sql.DB
// Our configuration from the ini
Cfg MailboxConfig
@@ -185,56 +184,6 @@ type VersionResponse struct {
FreeMessageQuota string `json:"free_message_quota"`
}
-type MailboxMetadata struct {
- // ORM
- gorm.Model `json:"-"`
-
- // ORM helper hash of signing key
- HashedSigningKey string `json:"-"`
-
- // The mailbox signing key.
- // Note that $H_MAILBOX == H(singingKey).
- // Note also how this key cannot be updated
- // as it identifies the mailbox.
- SigningKey string `json:"signing_key"`
-
- // Type of key.
- // Optional, as currently only
- // EdDSA keys are supported.
- SigningKeyType string `json:"signing_key_type"`
-
- // The mailbox encryption key.
- // This is an HPKE public key
- // in the X25519 format for use
- // in a X25519-DHKEM (RFC 9180).
- // Base32 crockford-encoded.
- EncryptionKey string `json:"encryption_key"`
-
- // Type of key.
- // Optional, as currently only
- // X25519 keys are supported.
- EncryptionKeyType string `json:"encryption_key_type"`
-
- // Expiration of this mapping.
- Expiration Timestamp `json:"expiration" gorm:"embedded;embeddedPrefix:expiration_"`
-
- // Keyoxide claim proof
- KoxClaimProof string `json:"kox_claim_proof,omitempty"`
-}
-
-type PendingMailboxRegistration struct {
- // ORM
- gorm.Model `json:"-"`
-
- // Hash of the inbox for this entry
- HashedSigningKey string // Requested registration duration
-
- Duration time.Duration
-
- // The order ID associated with this registration
- OrderID string `json:"-"`
-}
-
type MailboxRegistrationRequest struct {
// Keys to add/update for a mailbox.
@@ -261,17 +210,6 @@ type MailboxRateLimitedResponse struct {
Hint string `json:"hint"`
}
-type InboxEntry struct {
- // ORM
- gorm.Model `json:"-"`
-
- // Encrypted message. Must be exactly 256-32 bytes long.
- Body []byte
-
- // Hash of the inbox for this entry
- HashedSigningKey string
-}
-
func (m *Mailbox) configResponse(w http.ResponseWriter, r *http.Request) {
dpStr := m.Cfg.Ini.Section("mailbox").Key("delivery_period").MustString("72h")
dp, err := time.ParseDuration(dpStr)
@@ -292,6 +230,67 @@ func (m *Mailbox) configResponse(w http.ResponseWriter, r *http.Request) {
w.Write(response)
}
+// Get Hash-salted alias from database
+func GetMessagesCountFromDatabase(db *sql.DB, hashedKey string) (int64, error) {
+ query := `SELECT COUNT(*) AS num_messages
+ FROM taler_mailbox.inbox_entries
+ WHERE
+ "hashed_signing_key" = $1
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hashedKey)
+ if err != nil {
+ return 0, err
+ }
+ defer rows.Close()
+ if !rows.Next() {
+ return 0, nil
+ }
+ var res int64
+ err = rows.Scan(
+ &res,
+ )
+ if err != nil {
+ return 0, err
+ }
+ return res, nil
+}
+
+
+// Get Hash-salted alias from database
+func GetMessagesFromDatabase(db *sql.DB, hashedKey string, limit int) ([]InboxEntry, error) {
+ query := `SELECT
+ "serial",
+ "hashed_signing_key",
+ "body"
+ FROM taler_mailbox.inbox_entries
+ WHERE
+ "hashed_signing_key" = $1
+ LIMIT $2
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hashedKey, limit)
+ if err != nil {
+ return []InboxEntry{}, err
+ }
+ defer rows.Close()
+ var entries = make([]InboxEntry, 0)
+ for rows.Next() {
+ var e InboxEntry
+ err = rows.Scan(
+ &e.Serial,
+ &e.HashedSigningKey,
+ &e.Body,
+ )
+ if err != nil {
+ return entries, err
+ }
+ entries = append(entries, e)
+ }
+ return entries, nil
+}
+
+
func (m *Mailbox) getMessagesResponse(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
//to, toSet := vars["timeout_ms"]
@@ -300,7 +299,7 @@ func (m *Mailbox) getMessagesResponse(w http.ResponseWriter, r *http.Request) {
// FIXME timeout
// FIXME possibly limit results here
m.checkPendingRegistrationUpdates(vars["h_mailbox"])
- err := m.DB.Where("hashed_signing_key = ?", vars["h_mailbox"]).Limit(int(m.MessageResponseLimit)).Find(&entries).Error
+ entries, err := GetMessagesFromDatabase(m.DB, vars["h_mailbox"], int(m.MessageResponseLimit))
if err != nil {
log.Printf("%v", err)
w.WriteHeader(http.StatusNotFound)
@@ -311,7 +310,7 @@ func (m *Mailbox) getMessagesResponse(w http.ResponseWriter, r *http.Request) {
return
}
// Add ETag of first message ID
- etag := entries[0].ID
+ etag := entries[0].Serial
w.Header().Add("ETag", fmt.Sprintf("%d", etag))
for _, entry := range entries {
w.Write(entry.Body)
@@ -337,7 +336,7 @@ func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) {
}
if !m.MessageFee.IsZero() {
var count int64
- err = m.DB.Model(&InboxEntry{}).Where("hashed_signing_key = ?", vars["h_mailbox"]).Count(&count).Error
+ count, err = GetMessagesCountFromDatabase(m.DB, vars["h_mailbox"])
if nil != err {
m.Logf(LogError, "%v", err)
http.Error(w, "Cannot look for entries", http.StatusBadRequest)
@@ -350,14 +349,14 @@ func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) {
}
}
m.checkPendingRegistrationUpdates(vars["h_mailbox"])
- err = m.DB.First(&entry, "hashed_signing_key = ? AND body = ?", vars["h_mailbox"], body, true).Error
+ err = GetInboxEntryFromDatabaseBySigningKeyAndBody(m.DB, &entry, vars["h_mailbox"], body)
if err == nil {
w.WriteHeader(http.StatusNotModified)
return
}
entry.HashedSigningKey = vars["h_mailbox"]
entry.Body = body
- m.DB.Save(&entry)
+ err = InsertInboxEntryIntoDatabase(m.DB, &entry)
w.WriteHeader(http.StatusNoContent)
}
@@ -365,13 +364,13 @@ func (m *Mailbox) getKeysResponse(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var keyEntry MailboxMetadata
m.checkPendingRegistrationUpdates(vars["h_mailbox"])
- err := m.DB.First(&keyEntry, "hashed_signing_key = ?", vars["h_mailbox"]).Error
+ err := GetMailboxMetadataFromDatabaseBySigningKey(m.DB, &keyEntry, vars["h_mailbox"])
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
- m.Logf(LogDebug, "entry expires at %d, have %d", keyEntry.Expiration.Seconds, time.Now().Unix())
- if keyEntry.Expiration.Seconds < uint64(time.Now().Unix()) {
+ m.Logf(LogDebug, "entry expires at %d, have %d", keyEntry.Expiration, time.Now().Unix())
+ if keyEntry.Expiration < int64(time.Now().Unix()) {
w.WriteHeader(http.StatusNotFound)
return
}
@@ -395,7 +394,7 @@ func (m *Mailbox) validateRegistrationSignature(msg MailboxRegistrationRequest)
if nil != err {
return fmt.Errorf("unable to decode signature")
}
- binary.BigEndian.PutUint64(expNbo[:], msg.MailboxMetadata.Expiration.Seconds)
+ binary.BigEndian.PutUint64(expNbo[:], uint64(msg.MailboxMetadata.Expiration))
size := signedMsg[0:4]
binary.BigEndian.PutUint32(size, 64+4+4)
purp := signedMsg[4:8]
@@ -470,12 +469,12 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request
hMailbox := util.Base32CrockfordEncode(h.Sum(nil))
pendingRegistration.HashedSigningKey = hMailbox
// Round to the nearest multiple of a month
- reqExpiration := time.Unix(int64(msg.MailboxMetadata.Expiration.Seconds), 0)
+ reqExpiration := time.Unix(msg.MailboxMetadata.Expiration, 0)
now := time.Now()
reqDuration := reqExpiration.Sub(now).Round(monthDuration)
- err = m.DB.First(®istrationEntry, "hashed_signing_key = ?", hMailbox).Error
+ err = GetMailboxMetadataFromDatabaseBySigningKey(m.DB, ®istrationEntry, hMailbox)
if err == nil {
- // This probably meansthe registration is modified or extended or both
+ // This probably means the registration is modified or extended or both
entryModified := (registrationEntry.EncryptionKey != msg.MailboxMetadata.EncryptionKey)
// At least one MonthlyFee
if reqDuration.Microseconds() == 0 && !entryModified {
@@ -486,21 +485,31 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request
} else {
// Entry does not yet exist, add but immediately expire it
registrationEntry = msg.MailboxMetadata
- registrationEntry.Expiration.Seconds = uint64(time.Now().Unix() - 1)
+ registrationEntry.Expiration = time.Now().Unix() - 1
hAddr := sha512.New()
hAddr.Write(pk)
registrationEntry.HashedSigningKey = util.Base32CrockfordEncode(hAddr.Sum(nil))
- err = m.DB.Create(®istrationEntry).Error
+ err = InsertMailboxRegistrationIntoDatabase(m.DB, ®istrationEntry)
if nil != err {
w.WriteHeader(http.StatusInternalServerError)
return
}
}
- err = m.DB.First(&pendingRegistration, "hashed_signing_key = ?", hMailbox).Error
+ err = GetPendingRegistrationFromDatabaseBySigningKey(m.DB, &pendingRegistration, hMailbox)
pendingRegistrationExists := (nil == err)
if !pendingRegistrationExists {
pendingRegistration.HashedSigningKey = hMailbox
- pendingRegistration.Duration = reqDuration
+ pendingRegistration.Duration = reqDuration.Microseconds()
+ err = InsertPendingRegistrationIntoDatabase(m.DB, &pendingRegistration)
+ if nil != err {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ err = GetPendingRegistrationFromDatabaseBySigningKey(m.DB, &pendingRegistration, hMailbox)
+ if nil != err {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
}
// At least the update fee needs to be paid
cost, err := calculateCost(m.MonthlyFee.String(),
@@ -535,16 +544,21 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request
return
}
if len(payto) != 0 {
- m.DB.Save(&pendingRegistration)
+ err = UpdatePendingRegistrationOrderIdInDatabase(m.DB, &pendingRegistration)
+ if err != nil {
+ fmt.Println(err)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
w.WriteHeader(http.StatusPaymentRequired)
w.Header().Set("Taler", payto)
return
}
}
// Update expiration time of registration.
- registrationEntry.Expiration.Seconds += uint64(reqDuration.Seconds())
- m.DB.Delete(pendingRegistration)
- err = m.DB.Save(®istrationEntry).Error
+ registrationEntry.Expiration += int64(reqDuration.Seconds())
+ _, err = DeletePendingRegistrationFromDatabase(m.DB, &pendingRegistration)
+ err = UpdateMailboxExpirationInDatabase(m.DB, ®istrationEntry)
if nil != err {
w.WriteHeader(http.StatusInternalServerError)
return
@@ -555,7 +569,7 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request
func (m *Mailbox) checkPendingRegistrationUpdates(hMailbox string) {
var pendingEntry PendingMailboxRegistration
var registrationEntry MailboxMetadata
- err := m.DB.First(&pendingEntry, "hashed_signing_key = ?", hMailbox).Error
+ err := GetPendingRegistrationFromDatabaseBySigningKey(m.DB, &pendingEntry, hMailbox)
if err != nil {
return
}
@@ -565,7 +579,7 @@ func (m *Mailbox) checkPendingRegistrationUpdates(hMailbox string) {
if rc == http.StatusNotFound {
m.Logf(LogInfo, "Registration order for `%s' not found, removing\n", hMailbox)
}
- err := m.DB.Delete(&pendingEntry)
+ _, err = DeletePendingRegistrationFromDatabase(m.DB, &pendingEntry)
if nil != err {
m.Logf(LogInfo, "%v\n", err)
}
@@ -574,12 +588,15 @@ func (m *Mailbox) checkPendingRegistrationUpdates(hMailbox string) {
m.Logf(LogDebug, "Order status for %s is %s", pendingEntry.HashedSigningKey, orderStatus)
if merchant.OrderPaid == orderStatus {
m.Logf(LogDebug, "Order for %v appears to be paid", pendingEntry)
- err = m.DB.First(®istrationEntry, "hashed_signing_key = ?", hMailbox).Error
+ err = GetMailboxMetadataFromDatabaseBySigningKey(m.DB, ®istrationEntry, hMailbox)
if err == nil {
- m.Logf(LogDebug, "Adding %d seconds to entry expiration", uint64(pendingEntry.Duration.Seconds()))
- registrationEntry.Expiration.Seconds += uint64(pendingEntry.Duration.Seconds())
- m.DB.Save(®istrationEntry)
- err := m.DB.Delete(&pendingEntry)
+ m.Logf(LogDebug, "Adding %d seconds to entry expiration", pendingEntry.Duration)
+ registrationEntry.Expiration += pendingEntry.Duration
+ err = UpdateMailboxExpirationInDatabase(m.DB, ®istrationEntry)
+ if nil != err {
+ m.Logf(LogInfo, "%v\n", err)
+ }
+ _, err = DeletePendingRegistrationFromDatabase(m.DB, &pendingEntry)
if nil != err {
m.Logf(LogInfo, "%v\n", err)
}
@@ -632,8 +649,8 @@ func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request)
}
h := sha512.New()
h.Write(pkey)
- hHailbox := util.Base32CrockfordEncode(h.Sum(nil))
- m.checkPendingRegistrationUpdates(hHailbox)
+ hMailbox := util.Base32CrockfordEncode(h.Sum(nil))
+ m.checkPendingRegistrationUpdates(hMailbox)
var signedMsg [4 * 4]byte
binary.BigEndian.PutUint32(signedMsg[0:4], 4*4)
binary.BigEndian.PutUint32(signedMsg[4:8], gana.TalerSignaturePurposeMailboxMessagesDelete)
@@ -644,20 +661,19 @@ func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request)
return
}
// Check that expectedETag actually exists
- err = m.DB.Where("hashed_signing_key = ? AND id = ?", hHailbox, expectedETag).Find(&InboxEntry{}).Error
+ var entry InboxEntry
+ err = GetInboxEntryFromDatabaseBySerial(m.DB, &entry, hMailbox, int64(expectedETag))
if err != nil {
m.Logf(LogDebug, "Message to delete not found with ID %d", expectedETag)
w.WriteHeader(http.StatusNotFound)
return
}
- var entries []InboxEntry
- err = m.DB.Where("hashed_signing_key = ? AND id >= ?", hHailbox, expectedETag).Limit(count).Find(&entries).Error
+ num, err := DeleteInboxEntryFromSerialFromDatabase(m.DB, &entry)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
- m.Logf(LogDebug, "Found matching ID, deleting %d messages", len(entries))
- m.DB.Delete(entries)
+ m.Logf(LogDebug, "Found matching ID, deleted %d messages", num)
w.WriteHeader(http.StatusNoContent)
}
@@ -749,29 +765,17 @@ func (m *Mailbox) Initialize(cfg MailboxConfig) {
}
m.MessageFee = messageFee
m.FreeMessageQuota = cfg.Ini.Section("mailbox").Key("free_message_quota").MustUint64(0)
- _db, err := gorm.Open(cfg.DB, &gorm.Config{
- Logger: logger.Default.LogMode(logger.Silent),
- })
- if err != nil {
- panic(err)
- }
- m.DB = _db
- if err := m.DB.AutoMigrate(&InboxEntry{}); err != nil {
- panic(err)
- }
- if err := m.DB.AutoMigrate(&MailboxMetadata{}); err != nil {
- panic(err)
- }
+ m.DB = cfg.DB
go func() {
for {
- tx := m.DB.Where("expiration < ?", time.Now()).Delete(&MailboxMetadata{})
- m.Logf(LogInfo, "Cleaned up %d stale registrations.\n", tx.RowsAffected)
+ num, err := DeleteStaleRegistrationsFromDatabase(m.DB, time.Now())
+ if err != nil {
+ m.Logf(LogDebug, "Error purging stale registrations: `%v'.\n", err)
+ }
+ m.Logf(LogInfo, "Cleaned up %d stale registrations.\n", num)
time.Sleep(time.Hour * 24)
}
}()
- if err := m.DB.AutoMigrate(&PendingMailboxRegistration{}); err != nil {
- panic(err)
- }
// Clean up pending
pendingExpStr := cfg.Ini.Section("mailbox").Key("pending_registration_expiration").MustString("24h")
pendingExp, err := time.ParseDuration(pendingExpStr)
@@ -781,8 +785,11 @@ func (m *Mailbox) Initialize(cfg MailboxConfig) {
}
go func() {
for {
- tx := m.DB.Where("created_at < ?", time.Now().Add(-pendingExp)).Delete(&PendingMailboxRegistration{})
- m.Logf(LogInfo, "Cleaned up %d stale pending registrations.\n", tx.RowsAffected)
+ num, err := DeleteStalePendingRegistrationsFromDatabase(m.DB, time.Now().Add(-pendingExp))
+ if err != nil {
+ m.Logf(LogDebug, "Error purging stale registrations: `%v'.\n", err)
+ }
+ m.Logf(LogInfo, "Cleaned up %d stale pending registrations.\n", num)
time.Sleep(pendingExp)
}
}()