From 0e3655195a4a613064505e8cec5f011cf6b0759c Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 30 Apr 2024 09:18:04 +0300 Subject: [PATCH] replace email processing reactions; update deps --- bot/bot.go | 9 +- bot/command.go | 5 +- bot/email.go | 15 +- bot/mutex.go | 26 ++- go.mod | 14 +- go.sum | 28 +-- justfile | 1 + vendor/github.com/emersion/go-smtp/client.go | 13 +- vendor/github.com/emersion/go-smtp/conn.go | 77 ++++----- vendor/github.com/emersion/go-smtp/server.go | 4 +- .../etke.cc/go/healthchecks/v2/README.md | 38 +++- .../etke.cc/go/healthchecks/v2/auto.go | 5 - .../etke.cc/go/healthchecks/v2/client.go | 93 +++++++--- .../go/healthchecks/v2/healthchecks.go | 24 ++- .../etke.cc/go/healthchecks/v2/options.go | 15 ++ vendor/gitlab.com/etke.cc/go/psd/client.go | 6 +- .../etke.cc/linkpearl/.golangci.yml | 116 +++++++++---- vendor/gitlab.com/etke.cc/linkpearl/events.go | 48 ++++-- .../gitlab.com/etke.cc/linkpearl/linkpearl.go | 18 +- .../gitlab.com/etke.cc/linkpearl/reactions.go | 71 ++++++++ vendor/gitlab.com/etke.cc/linkpearl/send.go | 15 +- vendor/gitlab.com/etke.cc/linkpearl/sync.go | 10 +- vendor/gitlab.com/etke.cc/linkpearl/utils.go | 30 +--- vendor/go.mau.fi/util/dbutil/massinsert.go | 162 ++++++++++++++++++ vendor/go.mau.fi/util/dbutil/reflectscan.go | 30 ++++ vendor/maunium.net/go/mautrix/CHANGELOG.md | 17 ++ vendor/maunium.net/go/mautrix/client.go | 18 +- .../go/mautrix/crypto/cross_sign_store.go | 7 + .../mautrix/crypto/cross_sign_validation.go | 2 +- .../go/mautrix/crypto/keybackup.go | 6 +- .../go/mautrix/crypto/keysharing.go | 5 + vendor/maunium.net/go/mautrix/event/events.go | 3 +- .../go/mautrix/format/htmlparser.go | 9 +- vendor/maunium.net/go/mautrix/version.go | 2 +- vendor/modules.txt | 14 +- 35 files changed, 709 insertions(+), 247 deletions(-) create mode 100644 vendor/gitlab.com/etke.cc/linkpearl/reactions.go create mode 100644 vendor/go.mau.fi/util/dbutil/massinsert.go create mode 100644 vendor/go.mau.fi/util/dbutil/reflectscan.go diff --git a/bot/bot.go b/bot/bot.go index 9d82097..23bb86c 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -135,15 +135,10 @@ func (b *Bot) Start(statusMsg string) error { b.initSync() b.log.Info().Msg("Postmoogle has been started") - return b.lp.Start(statusMsg) + return b.lp.Start(ctx, statusMsg) } // Stop the bot func (b *Bot) Stop() { - ctx := context.Background() - err := b.lp.GetClient().SetPresence(ctx, event.PresenceOffline) - if err != nil { - b.log.Error().Err(err).Msg("cannot set presence = offline") - } - b.lp.GetClient().StopSync() + b.lp.Stop(context.Background()) } diff --git a/bot/command.go b/bot/command.go index 4453866..01cc8fe 100644 --- a/bot/command.go +++ b/bot/command.go @@ -624,7 +624,6 @@ func (b *Bot) runSendCommand(ctx context.Context, cfg config.Room, tos []string, from := cfg.Mailbox() + "@" + domain ID := email.MessageID(evt.ID, domain) for _, to := range tos { - recipients := []string{to} eml := email.New(ID, "", " "+ID, subject, from, to, to, "", body, htmlBody, nil, nil) data := eml.Compose(b.cfg.GetBot(ctx).DKIMPrivateKey()) if data == "" { @@ -634,14 +633,14 @@ func (b *Bot) runSendCommand(ctx context.Context, cfg config.Room, tos []string, queued, err := b.Sendmail(ctx, evt.ID, from, to, data) if queued { b.log.Warn().Err(err).Msg("email has been queued") - b.saveSentMetadata(ctx, queued, evt.ID, recipients, eml, cfg) + b.saveSentMetadata(ctx, queued, evt.ID, to, eml, cfg) continue } if err != nil { b.Error(ctx, "cannot send email to %s: %v", to, err) continue } - b.saveSentMetadata(ctx, false, evt.ID, recipients, eml, cfg) + b.saveSentMetadata(ctx, false, evt.ID, to, eml, cfg) } if len(tos) > 1 { b.lp.SendNotice(ctx, evt.RoomID, "All emails were sent.", linkpearl.RelatesTo(evt.ID, cfg.NoThreads())) diff --git a/bot/email.go b/bot/email.go index bc46369..cf81146 100644 --- a/bot/email.go +++ b/bot/email.go @@ -280,7 +280,7 @@ func (b *Bot) sendAutoreply(ctx context.Context, roomID id.RoomID, threadID id.E queued, err = b.Sendmail(ctx, evt.ID, meta.From, to, data) if queued { b.log.Info().Err(err).Str("from", meta.From).Str("to", to).Msg("email has been queued") - b.saveSentMetadata(ctx, queued, meta.ThreadID, recipients, eml, cfg, "Autoreply has been sent to "+to+" (queued)") + b.saveSentMetadata(ctx, queued, meta.ThreadID, to, eml, cfg, "Autoreply has been sent to "+to+" (queued)") continue } @@ -289,7 +289,7 @@ func (b *Bot) sendAutoreply(ctx context.Context, roomID id.RoomID, threadID id.E continue } - b.saveSentMetadata(ctx, queued, meta.ThreadID, recipients, eml, cfg, "Autoreply has been sent to "+to) + b.saveSentMetadata(ctx, queued, meta.ThreadID, to, eml, cfg, "Autoreply has been sent to "+to) } } @@ -364,7 +364,7 @@ func (b *Bot) SendEmailReply(ctx context.Context) { queued, err = b.Sendmail(ctx, evt.ID, meta.From, to, data) if queued { b.log.Info().Err(err).Str("from", meta.From).Str("to", to).Msg("email has been queued") - b.saveSentMetadata(ctx, queued, meta.ThreadID, recipients, eml, cfg) + b.saveSentMetadata(ctx, queued, meta.ThreadID, to, eml, cfg) continue } @@ -373,7 +373,7 @@ func (b *Bot) SendEmailReply(ctx context.Context) { continue } - b.saveSentMetadata(ctx, queued, meta.ThreadID, recipients, eml, cfg) + b.saveSentMetadata(ctx, queued, meta.ThreadID, to, eml, cfg) } } @@ -538,11 +538,10 @@ func (b *Bot) getParentEmail(ctx context.Context, evt *event.Event, newFromMailb // saveSentMetadata used to save metadata from !pm sent and thread reply events to a separate notice message // because that metadata is needed to determine email thread relations -func (b *Bot) saveSentMetadata(ctx context.Context, queued bool, threadID id.EventID, recipients []string, eml *email.Email, cfg config.Room, textOverride ...string) { - addrs := strings.Join(recipients, ", ") - text := "Email has been sent to " + addrs +func (b *Bot) saveSentMetadata(ctx context.Context, queued bool, threadID id.EventID, to string, eml *email.Email, cfg config.Room, textOverride ...string) { + text := "Email has been sent to " + to if queued { - text = "Email to " + addrs + " has been queued" + text = "Email to " + to + " has been queued" } if len(textOverride) > 0 { text = textOverride[0] diff --git a/bot/mutex.go b/bot/mutex.go index 317c6b8..adc4134 100644 --- a/bot/mutex.go +++ b/bot/mutex.go @@ -6,26 +6,22 @@ import ( "maunium.net/go/mautrix/id" ) -func (b *Bot) lock(ctx context.Context, roomID id.RoomID, optionalEventID ...id.EventID) { +const ( + reactionLock = "📨" + reactionUnlock = "✅" +) + +func (b *Bot) lock(ctx context.Context, roomID id.RoomID, eventID id.EventID) { b.mu.Lock(roomID.String()) - if len(optionalEventID) == 0 { - return - } - evtID := optionalEventID[0] - if _, err := b.lp.GetClient().SendReaction(ctx, roomID, evtID, "📨"); err != nil { - b.log.Error().Err(err).Str("roomID", roomID.String()).Str("eventID", evtID.String()).Msg("cannot send reaction on lock") + if err := b.lp.SendReaction(ctx, roomID, eventID, reactionLock); err != nil { + b.log.Error().Err(err).Str("roomID", roomID.String()).Str("eventID", eventID.String()).Msg("cannot send reaction on lock") } } -func (b *Bot) unlock(ctx context.Context, roomID id.RoomID, optionalEventID ...id.EventID) { +func (b *Bot) unlock(ctx context.Context, roomID id.RoomID, eventID id.EventID) { b.mu.Unlock(roomID.String()) - - if len(optionalEventID) == 0 { - return - } - evtID := optionalEventID[0] - if _, err := b.lp.GetClient().SendReaction(ctx, roomID, evtID, "✅"); err != nil { - b.log.Error().Err(err).Str("roomID", roomID.String()).Str("eventID", evtID.String()).Msg("cannot send reaction on unlock") + if err := b.lp.ReplaceReaction(ctx, roomID, eventID, reactionLock, reactionUnlock); err != nil { + b.log.Error().Err(err).Str("roomID", roomID.String()).Str("eventID", eventID.String()).Msg("cannot send reaction on unlock") } } diff --git a/go.mod b/go.mod index 361ffd1..1b55552 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/archdx/zerolog-sentry v1.8.2 github.com/emersion/go-msgauth v0.6.8 github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 - github.com/emersion/go-smtp v0.21.0 + github.com/emersion/go-smtp v0.21.1 github.com/fsnotify/fsnotify v1.7.0 github.com/gabriel-vasile/mimetype v1.4.3 github.com/getsentry/sentry-go v0.27.0 @@ -24,14 +24,14 @@ require ( github.com/rs/zerolog v1.32.0 gitlab.com/etke.cc/go/env v1.1.0 gitlab.com/etke.cc/go/fswatcher v1.0.0 - gitlab.com/etke.cc/go/healthchecks/v2 v2.0.0 + gitlab.com/etke.cc/go/healthchecks/v2 v2.2.0 gitlab.com/etke.cc/go/mxidwc v1.0.0 - gitlab.com/etke.cc/go/psd v1.1.1 + gitlab.com/etke.cc/go/psd v1.1.2 gitlab.com/etke.cc/go/secgen v1.2.0 gitlab.com/etke.cc/go/validator v1.0.7 - gitlab.com/etke.cc/linkpearl v0.0.0-20240316115913-106577b88942 - golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 - maunium.net/go/mautrix v0.18.0 + gitlab.com/etke.cc/linkpearl v0.0.0-20240425105001-435ae2720365 + golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f + maunium.net/go/mautrix v0.18.1 ) require ( @@ -56,7 +56,7 @@ require ( github.com/tidwall/sjson v1.2.5 // indirect github.com/yuin/goldmark v1.7.1 // indirect gitlab.com/etke.cc/go/trysmtp v1.1.3 // indirect - go.mau.fi/util v0.4.1 // indirect + go.mau.fi/util v0.4.2 // indirect golang.org/x/crypto v0.22.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.19.0 // indirect diff --git a/go.sum b/go.sum index 290bf75..726d71e 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ github.com/emersion/go-msgauth v0.6.8/go.mod h1:YDwuyTCUHu9xxmAeVj0eW4INnwB6NNZo github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY= github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= -github.com/emersion/go-smtp v0.21.0 h1:ZDZmX9aFUuPlD1lpoT0nC/nozZuIkSCyQIyxdijjCy0= -github.com/emersion/go-smtp v0.21.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/emersion/go-smtp v0.21.1 h1:VQeZSZAKk8ueYii1yR5Zalmy7jI287eWDUqSaJ68vRM= +github.com/emersion/go-smtp v0.21.1/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -98,26 +98,26 @@ gitlab.com/etke.cc/go/env v1.1.0 h1:nbMhZkMu6C8lysRlb5siIiylWuyVkGAgEvwWEqz/82o= gitlab.com/etke.cc/go/env v1.1.0/go.mod h1:e1l4RM5MA1sc0R1w/RBDAESWRwgo5cOG9gx8BKUn2C4= gitlab.com/etke.cc/go/fswatcher v1.0.0 h1:uyiVn+1NVCjOLZrXSZouIDBDZBMwVipS4oYuvAFpPzo= gitlab.com/etke.cc/go/fswatcher v1.0.0/go.mod h1:MqTOxyhXfvaVZQUL9/Ksbl2ow1PTBVu3eqIldvMq0RE= -gitlab.com/etke.cc/go/healthchecks/v2 v2.0.0 h1:/VX2V/I0kH0Yah546EHcOZkuxbEj+8FBmsnb5uOXGUw= -gitlab.com/etke.cc/go/healthchecks/v2 v2.0.0/go.mod h1:DdNc1ESc1cAgOdsIwxxV+RUWgn6ewCpfFKzLuF0kSfc= +gitlab.com/etke.cc/go/healthchecks/v2 v2.2.0 h1:7Y0qm8OAqeJApDNNfGSeA6WFF60q/394gMKinZNbSWE= +gitlab.com/etke.cc/go/healthchecks/v2 v2.2.0/go.mod h1:DdNc1ESc1cAgOdsIwxxV+RUWgn6ewCpfFKzLuF0kSfc= gitlab.com/etke.cc/go/mxidwc v1.0.0 h1:6EAlJXvs3nU4RaMegYq6iFlyVvLw7JZYnZmNCGMYQP0= gitlab.com/etke.cc/go/mxidwc v1.0.0/go.mod h1:E/0kh45SAN9+ntTG0cwkAEKdaPxzvxVmnjwivm9nmz8= -gitlab.com/etke.cc/go/psd v1.1.1 h1:UIL0X+thvYaeBTX8/G6lilqAToGCypihujGu5gtK5zQ= -gitlab.com/etke.cc/go/psd v1.1.1/go.mod h1:6b444NOkXlZ1n7WLCNazAkOC2bHPgqgfB9earThwKPk= +gitlab.com/etke.cc/go/psd v1.1.2 h1:nplS8Sc8tV+G/zq0QtC0pfW97EI8v3ZiSoYOYKCTbjY= +gitlab.com/etke.cc/go/psd v1.1.2/go.mod h1:6b444NOkXlZ1n7WLCNazAkOC2bHPgqgfB9earThwKPk= gitlab.com/etke.cc/go/secgen v1.2.0 h1:qpV7rUn5Rs6eWxAmbGG/idPCOgsN4HggGmSZ+1R/L70= gitlab.com/etke.cc/go/secgen v1.2.0/go.mod h1:v5L07AIXtNpC/miYiK0TMIn+ZKbiYrTRiXTw6qTL6pw= gitlab.com/etke.cc/go/trysmtp v1.1.3 h1:e2EHond77onMaecqCg6mWumffTSEf+ycgj88nbeefDI= gitlab.com/etke.cc/go/trysmtp v1.1.3/go.mod h1:lOO7tTdAE0a3ETV3wN3GJ7I1Tqewu7YTpPWaOmTteV0= gitlab.com/etke.cc/go/validator v1.0.7 h1:4BGDTa9x68vJhbyn7m8W2yX+2Nb5im9+JLRrgoLUlF4= gitlab.com/etke.cc/go/validator v1.0.7/go.mod h1:Id0SxRj0J3IPhiKlj0w1plxVLZfHlkwipn7HfRZsDts= -gitlab.com/etke.cc/linkpearl v0.0.0-20240316115913-106577b88942 h1:hhDXBsDcYgAit9gwfvawnPXMIwHNKL9DL1kCCyyzB8A= -gitlab.com/etke.cc/linkpearl v0.0.0-20240316115913-106577b88942/go.mod h1:0AIH2o0fi4WoZhMw+tW63rrcI5aERH9c34RVHQXn1Q0= -go.mau.fi/util v0.4.1 h1:3EC9KxIXo5+h869zDGf5OOZklRd/FjeVnimTwtm3owg= -go.mau.fi/util v0.4.1/go.mod h1:GjkTEBsehYZbSh2LlE6cWEn+6ZIZTGrTMM/5DMNlmFY= +gitlab.com/etke.cc/linkpearl v0.0.0-20240425105001-435ae2720365 h1:MZnAxKJ5Bwv6ZHfC+HUEQyTDC0HPXVB8Q63ISkZ1lHo= +gitlab.com/etke.cc/linkpearl v0.0.0-20240425105001-435ae2720365/go.mod h1:onM3Tge1SUII+oda91/ThDvVvMhkPiSRJ7EOsC2g0yc= +go.mau.fi/util v0.4.2 h1:RR3TOcRHmCF9Bx/3YG4S65MYfa+nV6/rn8qBWW4Mi30= +go.mau.fi/util v0.4.2/go.mod h1:PlAVfUUcPyHPrwnvjkJM9UFcPE7qGPDJqk+Oufa1Gtw= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= -golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= @@ -133,5 +133,5 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -maunium.net/go/mautrix v0.18.0 h1:sNsApeSWB8x0hLjGcdmi5JqO6Tvp2PVkiSStz+Yas6k= -maunium.net/go/mautrix v0.18.0/go.mod h1:STwJZ+6CAeiEQs7fYCkd5aC12XR5DXANE6Swy/PBKGo= +maunium.net/go/mautrix v0.18.1 h1:a6mUsJixegBNTXUoqC5RQ9gsumIPzKvCubKwF+zmCt4= +maunium.net/go/mautrix v0.18.1/go.mod h1:2oHaq792cSXFGvxLvYw3Gf1L4WVVP4KZcYys5HVk/h8= diff --git a/justfile b/justfile index e3de892..1d4afa7 100644 --- a/justfile +++ b/justfile @@ -54,3 +54,4 @@ login: docker: docker buildx create --use docker buildx build --pull --provenance=false --platform {{ platforms }} --push -t {{ gitlab_image }} -t {{ etke_image }} . + docker buildx rm diff --git a/vendor/github.com/emersion/go-smtp/client.go b/vendor/github.com/emersion/go-smtp/client.go index c40e625..66249b5 100644 --- a/vendor/github.com/emersion/go-smtp/client.go +++ b/vendor/github.com/emersion/go-smtp/client.go @@ -44,9 +44,7 @@ type Client struct { // 30 seconds was chosen as it's the same duration as http.DefaultTransport's // timeout. -const defaultTimeout = 30 * time.Second - -var defaultDialer = net.Dialer{Timeout: defaultTimeout} +var defaultDialer = net.Dialer{Timeout: 30 * time.Second} // Dial returns a new Client connected to an SMTP server at addr. The addr must // include a port, as in "mail.example.com:smtp". @@ -358,8 +356,13 @@ func (c *Client) Auth(a sasl.Client) error { if err != nil { return err } - resp64 := make([]byte, encoding.EncodedLen(len(resp))) - encoding.Encode(resp64, resp) + var resp64 []byte + if len(resp) > 0 { + resp64 = make([]byte, encoding.EncodedLen(len(resp))) + encoding.Encode(resp64, resp) + } else if resp != nil { + resp64 = []byte{'='} + } code, msg64, err := c.cmd(0, strings.TrimSpace(fmt.Sprintf("AUTH %s %s", mech, resp64))) for err == nil { var msg []byte diff --git a/vendor/github.com/emersion/go-smtp/conn.go b/vendor/github.com/emersion/go-smtp/conn.go index 3e9a056..57e7c9c 100644 --- a/vendor/github.com/emersion/go-smtp/conn.go +++ b/vendor/github.com/emersion/go-smtp/conn.go @@ -232,12 +232,7 @@ func (c *Conn) handleGreet(enhanced bool, arg string) { sess, err := c.server.Backend.NewSession(c) if err != nil { c.helo = "" - - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - return - } - c.writeResponse(451, EnhancedCode{4, 0, 0}, err.Error()) + c.writeError(451, EnhancedCode{4, 0, 0}, err) return } @@ -421,11 +416,7 @@ func (c *Conn) handleMail(arg string) { } if err := c.Session().Mail(from, opts); err != nil { - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - return - } - c.writeResponse(451, EnhancedCode{4, 0, 0}, err.Error()) + c.writeError(451, EnhancedCode{4, 0, 0}, err) return } @@ -725,11 +716,7 @@ func (c *Conn) handleRcpt(arg string) { } if err := c.Session().Rcpt(recipient, opts); err != nil { - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - return - } - c.writeResponse(451, EnhancedCode{4, 0, 0}, err.Error()) + c.writeError(451, EnhancedCode{4, 0, 0}, err) return } c.recipients = append(c.recipients, recipient) @@ -786,21 +773,21 @@ func (c *Conn) handleAuth(arg string) { // Parse client initial response if there is one var ir []byte if len(parts) > 1 { - var err error - ir, err = base64.StdEncoding.DecodeString(parts[1]) - if err != nil { - c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data") - return + if parts[1] == "=" { + ir = []byte{} + } else { + var err error + ir, err = base64.StdEncoding.DecodeString(parts[1]) + if err != nil { + c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data") + return + } } } sasl, err := c.auth(mechanism) if err != nil { - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - } else { - c.writeResponse(454, EnhancedCode{4, 7, 0}, err.Error()) - } + c.writeError(454, EnhancedCode{4, 7, 0}, err) return } @@ -808,11 +795,7 @@ func (c *Conn) handleAuth(arg string) { for { challenge, done, err := sasl.Next(response) if err != nil { - if smtpErr, ok := err.(*SMTPError); ok { - c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) - return - } - c.writeResponse(454, EnhancedCode{4, 7, 0}, err.Error()) + c.writeError(454, EnhancedCode{4, 7, 0}, err) return } @@ -837,10 +820,14 @@ func (c *Conn) handleAuth(arg string) { return } - response, err = base64.StdEncoding.DecodeString(encoded) - if err != nil { - c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data") - return + if encoded == "=" { + response = []byte{} + } else { + response, err = base64.StdEncoding.DecodeString(encoded) + if err != nil { + c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data") + return + } } } @@ -930,7 +917,7 @@ func (c *Conn) handleData(arg string) { } r := newDataReader(c) - code, enhancedCode, msg := toSMTPStatus(c.Session().Data(r)) + code, enhancedCode, msg := dataErrorToStatus(c.Session().Data(r)) r.limited = false io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed c.writeResponse(code, enhancedCode, msg) @@ -1027,7 +1014,7 @@ func (c *Conn) handleBdat(arg string) { // the whole chunk. io.Copy(ioutil.Discard, chunk) - c.writeResponse(toSMTPStatus(err)) + c.writeResponse(dataErrorToStatus(err)) if err == errPanic { c.Close() @@ -1050,11 +1037,11 @@ func (c *Conn) handleBdat(arg string) { if c.server.LMTP { c.bdatStatus.fillRemaining(err) for i, rcpt := range c.recipients { - code, enchCode, msg := toSMTPStatus(<-c.bdatStatus.status[i]) + code, enchCode, msg := dataErrorToStatus(<-c.bdatStatus.status[i]) c.writeResponse(code, enchCode, "<"+rcpt+"> "+msg) } } else { - c.writeResponse(toSMTPStatus(err)) + c.writeResponse(dataErrorToStatus(err)) } if err == errPanic { @@ -1189,7 +1176,7 @@ func (c *Conn) handleDataLMTP() { } for i, rcpt := range c.recipients { - code, enchCode, msg := toSMTPStatus(<-status.status[i]) + code, enchCode, msg := dataErrorToStatus(<-status.status[i]) c.writeResponse(code, enchCode, "<"+rcpt+"> "+msg) } @@ -1200,7 +1187,7 @@ func (c *Conn) handleDataLMTP() { } } -func toSMTPStatus(err error) (code int, enchCode EnhancedCode, msg string) { +func dataErrorToStatus(err error) (code int, enchCode EnhancedCode, msg string) { if err != nil { if smtperr, ok := err.(*SMTPError); ok { return smtperr.Code, smtperr.EnhancedCode, smtperr.Message @@ -1253,6 +1240,14 @@ func (c *Conn) writeResponse(code int, enhCode EnhancedCode, text ...string) { } } +func (c *Conn) writeError(code int, enhCode EnhancedCode, err error) { + if smtpErr, ok := err.(*SMTPError); ok { + c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) + } else { + c.writeResponse(code, enhCode, err.Error()) + } +} + // Reads a line of input func (c *Conn) readLine() (string, error) { if c.server.ReadTimeout != 0 { diff --git a/vendor/github.com/emersion/go-smtp/server.go b/vendor/github.com/emersion/go-smtp/server.go index f92e109..b76e7d2 100644 --- a/vendor/github.com/emersion/go-smtp/server.go +++ b/vendor/github.com/emersion/go-smtp/server.go @@ -12,9 +12,7 @@ import ( "time" ) -var ( - ErrServerClosed = errors.New("smtp: server already closed") -) +var ErrServerClosed = errors.New("smtp: server already closed") // Logger interface is used by Server to report unexpected internal errors. type Logger interface { diff --git a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/README.md b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/README.md index 14b2157..aca5b18 100644 --- a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/README.md +++ b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/README.md @@ -1,15 +1,37 @@ # healthchecks -A [healthchecks.io](https://github.com/healthchecks/healthchecks) client +A fully async [healthchecks.io](https://github.com/healthchecks/healthchecks) golang client, with lots of features, some highlights: -check the godoc for information +* Highly configurable: `WithHTTPClient()`, `WithBaseURL()`, `WithUserAgent()`, `WithErrLog()`, `WithCheckUUID()`, `WithAutoProvision()`, etc. +* Automatic determination of HTTP method (`POST`, `HEAD`) based on body existence +* Auto mode: just call `client.Auto(time.Duration)` and client will send `Success()` request automatically with specified frequency +* Global mode: init client once with `healthchecks.New()`, and access it from anywhere by calling `healthchecks.Global()` + +Check [godoc](https://pkg.go.dev/gitlab.com/etke.cc/go/healthchecks/v2) for more details. ```go -hc := healthchecks.New( - healthchecks.WithCheckUUID("your-uuid"), -) -go hc.Auto() +package main -hc.Log(strings.NewReader("optional body you can attach to any action")) -hc.Shutdown() +import ( + "time" + + "gitlab.com/etke.cc/go/healthchecks/v2" +) + +var hc *healthchecks.Client + +func main() { + hc = healthchecks.New( + healthchecks.WithCheckUUID("CHECK_UUID") + ) + defer hc.Shutdown() + // send basic success request + hc.Success() + + // or use auto mode, that will send success request with the specified frequency + go hc.Auto(1*time.Minute) + + // need to call the client from another place in your project? + // just call healthchecks.Global() and you will get the same client +} ``` diff --git a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/auto.go b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/auto.go index f2ab613..76a533b 100644 --- a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/auto.go +++ b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/auto.go @@ -17,8 +17,3 @@ func (c *Client) Auto(every time.Duration) { } } } - -// Shutdown the client -func (c *Client) Shutdown() { - c.done <- true -} diff --git a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/client.go b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/client.go index 874c785..4579f6c 100644 --- a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/client.go +++ b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/client.go @@ -5,39 +5,46 @@ import ( "io" "net/http" "strconv" + "sync" "time" "github.com/google/uuid" ) // Client for healthchecks +// if client initialized without any options, it will be disabled by default, +// but you can override it by calling SetEnabled(true). type Client struct { - http *http.Client - log func(string, error) - baseURL string - uuid string - rid string - create bool - done chan bool + wg sync.WaitGroup + enabled bool + http *http.Client + log func(string, error) + userAgent string + baseURL string + uuid string + rid string + create bool + done chan bool } // init client func (c *Client) init(options ...Option) { + c.enabled = true + c.log = DefaultErrLog + c.baseURL = DefaultAPI + c.userAgent = DefaultUserAgent + c.http = &http.Client{Timeout: 10 * time.Second} + c.done = make(chan bool, 1) + c.uuid = "" + + if len(options) == 0 { + c.enabled = false + } + for _, option := range options { option(c) } - if c.log == nil { - c.log = DefaultErrLog - } - if c.baseURL == "" { - c.baseURL = DefaultAPI - } - if c.http == nil { - c.http = &http.Client{Timeout: 10 * time.Second} - } - if c.done == nil { - c.done = make(chan bool, 1) - } + if c.uuid == "" { randomUUID, _ := uuid.NewRandom() c.uuid = randomUUID.String() @@ -46,22 +53,39 @@ func (c *Client) init(options ...Option) { } } +// call API func (c *Client) call(operation, endpoint string, body ...io.Reader) { - var err error - var resp *http.Response + if !c.enabled { + return + } + + c.wg.Add(1) + defer c.wg.Done() + targetURL := fmt.Sprintf("%s/%s%s?rid=%s", c.baseURL, c.uuid, endpoint, c.rid) if c.create { targetURL += "&create=1" } + + var req *http.Request + var err error if len(body) > 0 { - resp, err = c.http.Post(targetURL, "text/plain; charset=utf-8", body[0]) + req, err = http.NewRequest(http.MethodPost, targetURL, body[0]) } else { - resp, err = c.http.Head(targetURL) + req, err = http.NewRequest(http.MethodHead, targetURL, http.NoBody) } if err != nil { c.log(operation, err) return } + req.Header.Set("User-Agent", c.userAgent) + req.Header.Set("Content-Type", "text/plain; charset=utf-8") + + resp, err := c.http.Do(req) + if err != nil { + c.log(operation, err) + return + } defer resp.Body.Close() if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { @@ -76,27 +100,40 @@ func (c *Client) call(operation, endpoint string, body ...io.Reader) { } } +// SetEnabled sets the enabled flag, ignoring the options +// if client initialized without any options, it will be disabled by default, +// but you can override it by calling SetEnabled(true). +func (c *Client) SetEnabled(enabled bool) { + c.enabled = enabled +} + // Start signal means the job started func (c *Client) Start(optionalBody ...io.Reader) { - c.call("start", "/start", optionalBody...) + go c.call("start", "/start", optionalBody...) } // Success signal means the job has completed successfully (or, a continuously running process is still running and healthy). func (c *Client) Success(optionalBody ...io.Reader) { - c.call("success", "", optionalBody...) + go c.call("success", "", optionalBody...) } // Fail signal means the job failed func (c *Client) Fail(optionalBody ...io.Reader) { - c.call("fail", "/fail", optionalBody...) + go c.call("fail", "/fail", optionalBody...) } // Log signal just adds an event to the job log, without changing job status func (c *Client) Log(optionalBody ...io.Reader) { - c.call("log", "/log", optionalBody...) + go c.call("log", "/log", optionalBody...) } // ExitStatus signal sends job's exit code (0-255) func (c *Client) ExitStatus(exitCode int, optionalBody ...io.Reader) { - c.call("exit status", "/"+strconv.Itoa(exitCode), optionalBody...) + go c.call("exit status", "/"+strconv.Itoa(exitCode), optionalBody...) +} + +// Shutdown the client +func (c *Client) Shutdown() { + c.done <- true + c.wg.Wait() } diff --git a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/healthchecks.go b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/healthchecks.go index dfb12b9..a369fa7 100644 --- a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/healthchecks.go +++ b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/healthchecks.go @@ -6,12 +6,19 @@ import ( "github.com/google/uuid" ) -// DefaultAPI base url for checks -const DefaultAPI = "https://hc-ping.com" +const ( + // DefaultAPI base url for checks + DefaultAPI = "https://hc-ping.com" + // DefaultUserAgent for the client + DefaultUserAgent = "Go-Healthchecks (lib; +https://gitlab.com/etke.cc/go/healthchecks)" +) // ErrLog used to log errors occurred during an operation type ErrLog func(operation string, err error) +// global client +var global *Client + // DefaultErrLog if you don't provide one yourself var DefaultErrLog = func(operation string, err error) { fmt.Printf("healtchecks operation %q failed: %v\n", operation, err) @@ -25,5 +32,18 @@ func New(options ...Option) *Client { } c.init(options...) + if global == nil { + global = c + } + return c } + +// Global healthchecks client +func Global() *Client { + if global == nil { + global = New() + } + + return global +} diff --git a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/options.go b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/options.go index 85fa308..85a9971 100644 --- a/vendor/gitlab.com/etke.cc/go/healthchecks/v2/options.go +++ b/vendor/gitlab.com/etke.cc/go/healthchecks/v2/options.go @@ -2,6 +2,7 @@ package healthchecks import "net/http" +// Option for healthchecks client type Option func(*Client) // WithHTTPClient sets the http client @@ -18,6 +19,13 @@ func WithBaseURL(baseURL string) Option { } } +// WithUserAgent sets the user agent +func WithUserAgent(userAgent string) Option { + return func(c *Client) { + c.userAgent = userAgent + } +} + // WithErrLog sets the error log func WithErrLog(errLog ErrLog) Option { return func(c *Client) { @@ -39,6 +47,13 @@ func WithAutoProvision() Option { } } +// WithGlobal sets this client as the global client +func WithGlobal() Option { + return func(c *Client) { + global = c + } +} + // WithDone sets the done channel func WithDone(done chan bool) Option { return func(c *Client) { diff --git a/vendor/gitlab.com/etke.cc/go/psd/client.go b/vendor/gitlab.com/etke.cc/go/psd/client.go index 23f016f..d0bf849 100644 --- a/vendor/gitlab.com/etke.cc/go/psd/client.go +++ b/vendor/gitlab.com/etke.cc/go/psd/client.go @@ -53,13 +53,17 @@ func (p *Client) GetWithContext(ctx context.Context, identifier string) ([]*Targ return nil, err } req.SetBasicAuth(p.login, p.password) - req.Header.Set("User-Agent", "Go-psd-client/"+version) + req.Header.Set("User-Agent", "Go-PSD-client/"+version) resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { + if resp.StatusCode == http.StatusGone { // not found, to distinguish from reverse proxy 404 error + return nil, nil + } + err = fmt.Errorf("%s", resp.Status) //nolint:goerr113 // that's ok return nil, err } diff --git a/vendor/gitlab.com/etke.cc/linkpearl/.golangci.yml b/vendor/gitlab.com/etke.cc/linkpearl/.golangci.yml index 84ad29e..2120503 100644 --- a/vendor/gitlab.com/etke.cc/linkpearl/.golangci.yml +++ b/vendor/gitlab.com/etke.cc/linkpearl/.golangci.yml @@ -1,85 +1,132 @@ run: concurrency: 4 - timeout: 5m + timeout: 30m issues-exit-code: 1 tests: true build-tags: [] - skip-dirs: [] skip-dirs-use-default: true skip-files: [] modules-download-mode: readonly - allow-parallel-runners: false output: - format: colored-line-number + formats: + - format: colored-line-number print-issued-lines: true print-linter-name: true - uniq-by-line: true - path-prefix: "" sort-results: true linters-settings: + decorder: + dec-order: + - const + - var + - type + - func + dogsled: + max-blank-identifiers: 3 errcheck: + check-type-assertions: true check-blank: true - gci: - local-prefixes: gitlab.com/etke.cc/linkpearl + errchkjson: + report-no-exported: true + exhaustive: + check: + - switch + - map + default-signifies-exhaustive: true gocognit: - min-complexity: 10 + min-complexity: 15 nestif: - min-complexity: 4 + min-complexity: 5 gocritic: enabled-tags: + - diagnostic + - style - performance + gofmt: + simplify: true + rewrite-rules: + - pattern: 'interface{}' + replacement: 'any' + - pattern: 'a[b:len(a)]' + replacement: 'a[b:]' gofumpt: - lang-version: "1.19" - goimports: - local-prefixes: gitlab.com/etke.cc/linkpearl - gosimple: - go: "1.19" - checks: [ "all" ] - govet: - check-shadowing: true - enable: - - atomicalign - - shadow + extra-rules: true + grouper: + const-require-single-const: true + import-require-single-import: true + var-require-single-var: true misspell: locale: US - staticcheck: - go: "1.19" - checks: [ "all" ] - stylecheck: - go: "1.19" + usestdlibvars: + time-month: true + time-layout: true + crypto-hash: true + default-rpc-path: true + os-dev-null: true + sql-isolation-level: true + tls-signature-scheme: true + constant-kind: true unparam: check-exported: true - unused: - go: "1.19" linters: disable-all: false enable: - - megacheck - - govet + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - decorder + - dogsled + - dupl + - dupword + - durationcheck - errcheck - - gci + - errchkjson + - errname + - errorlint + - execinquery + - exhaustive + - exportloopref + - forcetypeassert - gocognit - - nestif - gocritic + - gocyclo + - gofmt - gofumpt - goimports + - gosec - gosimple + - gosmopolitan - govet + - ineffassign + - makezero + - mirror - misspell + - nestif + - nolintlint + - prealloc + - predeclared + - revive + - sqlclosecheck - staticcheck - - stylecheck + - unconvert - unparam - unused + - usestdlibvars + - wastedassign fast: false issues: + exclude-dirs: + - mocks exclude-rules: - path: _test\.go linters: - gocyclo + - gocognit - errcheck - dupl - gosec @@ -89,6 +136,9 @@ issues: - linters: - lll source: "^//go:generate " + - linters: + - revive + text: "returns unexported type" max-issues-per-linter: 0 max-same-issues: 0 new: false diff --git a/vendor/gitlab.com/etke.cc/linkpearl/events.go b/vendor/gitlab.com/etke.cc/linkpearl/events.go index e9531c1..3fa4ea5 100644 --- a/vendor/gitlab.com/etke.cc/linkpearl/events.go +++ b/vendor/gitlab.com/etke.cc/linkpearl/events.go @@ -15,6 +15,12 @@ type RespThreads struct { NextBatch string `json:"next_batch"` } +// RespRelations is response of https://spec.matrix.org/v1.8/client-server-api/#get_matrixclientv1roomsroomidrelationseventidreltype +type RespRelations struct { + Chunk []*event.Event `json:"chunk"` + NextBatch string `json:"next_batch"` +} + // Threads endpoint, ref: https://spec.matrix.org/v1.8/client-server-api/#get_matrixclientv1roomsroomidthreads func (l *Linkpearl) Threads(ctx context.Context, roomID id.RoomID, fromToken ...string) (*RespThreads, error) { var from string @@ -33,8 +39,26 @@ func (l *Linkpearl) Threads(ctx context.Context, roomID id.RoomID, fromToken ... return resp, UnwrapError(err) } +// Relations returns all relations of the given type for the given event +func (l *Linkpearl) Relations(ctx context.Context, roomID id.RoomID, eventID id.EventID, relType string, fromToken ...string) (*RespRelations, error) { + var from string + if len(fromToken) > 0 { + from = fromToken[0] + } + + query := map[string]string{ + "from": from, + "limit": "100", + } + + var resp *RespRelations + urlPath := l.GetClient().BuildURLWithQuery(mautrix.ClientURLPath{"v1", "rooms", roomID, "relations", eventID, relType}, query) + _, err := l.GetClient().MakeRequest(ctx, "GET", urlPath, nil, &resp) + return resp, UnwrapError(err) +} + // FindThreadBy tries to find thread message event by field and value -func (l *Linkpearl) FindThreadBy(ctx context.Context, roomID id.RoomID, field, value string, fromToken ...string) *event.Event { +func (l *Linkpearl) FindThreadBy(ctx context.Context, roomID id.RoomID, fieldValue map[string]string, fromToken ...string) *event.Event { var from string if len(fromToken) > 0 { from = fromToken[0] @@ -48,9 +72,11 @@ func (l *Linkpearl) FindThreadBy(ctx context.Context, roomID id.RoomID, field, v } for _, msg := range resp.Chunk { - evt, contains := l.eventContains(ctx, msg, field, value) - if contains { - return evt + for field, value := range fieldValue { + evt, contains := l.eventContains(ctx, msg, field, value) + if contains { + return evt + } } } @@ -58,11 +84,11 @@ func (l *Linkpearl) FindThreadBy(ctx context.Context, roomID id.RoomID, field, v return nil } - return l.FindThreadBy(ctx, roomID, field, value, resp.NextBatch) + return l.FindThreadBy(ctx, roomID, fieldValue, resp.NextBatch) } // FindEventBy tries to find message event by field and value -func (l *Linkpearl) FindEventBy(ctx context.Context, roomID id.RoomID, field, value string, fromToken ...string) *event.Event { +func (l *Linkpearl) FindEventBy(ctx context.Context, roomID id.RoomID, fieldValue map[string]string, fromToken ...string) *event.Event { var from string if len(fromToken) > 0 { from = fromToken[0] @@ -76,9 +102,11 @@ func (l *Linkpearl) FindEventBy(ctx context.Context, roomID id.RoomID, field, va } for _, msg := range resp.Chunk { - evt, contains := l.eventContains(ctx, msg, field, value) - if contains { - return evt + for field, value := range fieldValue { + evt, contains := l.eventContains(ctx, msg, field, value) + if contains { + return evt + } } } @@ -86,7 +114,7 @@ func (l *Linkpearl) FindEventBy(ctx context.Context, roomID id.RoomID, field, va return nil } - return l.FindEventBy(ctx, roomID, field, value, resp.End) + return l.FindEventBy(ctx, roomID, fieldValue, resp.End) } func (l *Linkpearl) eventContains(ctx context.Context, evt *event.Event, field, value string) (*event.Event, bool) { diff --git a/vendor/gitlab.com/etke.cc/linkpearl/linkpearl.go b/vendor/gitlab.com/etke.cc/linkpearl/linkpearl.go index db7886f..9905fd4 100644 --- a/vendor/gitlab.com/etke.cc/linkpearl/linkpearl.go +++ b/vendor/gitlab.com/etke.cc/linkpearl/linkpearl.go @@ -21,6 +21,8 @@ const ( DefaultAccountDataCache = 1000 // DefaultEventsLimit for methods like lp.Threads() and lp.FindEventBy() DefaultEventsLimit = 1000 + // DefaultTypingTimeout in seconds for typing notifications + DefaultTypingTimeout = 60 ) // Linkpearl object @@ -104,7 +106,7 @@ func New(cfg *Config) (*Linkpearl, error) { return nil, err } lp.ch.LoginAs = cfg.LoginAs() - if err = lp.ch.Init(context.Background()); err != nil { + if err := lp.ch.Init(context.Background()); err != nil { return nil, err } lp.api.Crypto = lp.ch @@ -140,33 +142,33 @@ func (l *Linkpearl) SetPresence(ctx context.Context, presence event.Presence, me return err } -// SetJoinPermit sets the the join permit callback function +// SetJoinPermit sets the join permit callback function func (l *Linkpearl) SetJoinPermit(value func(context.Context, *event.Event) bool) { l.joinPermit = value } // Start performs matrix /sync -func (l *Linkpearl) Start(optionalStatusMsg ...string) error { +func (l *Linkpearl) Start(ctx context.Context, optionalStatusMsg ...string) error { l.initSync() var statusMsg string if len(optionalStatusMsg) > 0 { statusMsg = optionalStatusMsg[0] } - err := l.SetPresence(context.Background(), event.PresenceOnline, statusMsg) + err := l.SetPresence(ctx, event.PresenceOnline, statusMsg) if err != nil { l.log.Error().Err(err).Msg("cannot set presence") } - defer l.Stop() + defer l.Stop(ctx) l.log.Info().Msg("client has been started") - return l.api.Sync() + return l.api.SyncWithContext(ctx) } // Stop the client -func (l *Linkpearl) Stop() { +func (l *Linkpearl) Stop(ctx context.Context) { l.log.Debug().Msg("stopping the client") - if err := l.api.SetPresence(context.Background(), event.PresenceOffline); err != nil { + if err := l.api.SetPresence(ctx, event.PresenceOffline); err != nil { l.log.Error().Err(err).Msg("cannot set presence") } l.api.StopSync() diff --git a/vendor/gitlab.com/etke.cc/linkpearl/reactions.go b/vendor/gitlab.com/etke.cc/linkpearl/reactions.go new file mode 100644 index 0000000..52dab84 --- /dev/null +++ b/vendor/gitlab.com/etke.cc/linkpearl/reactions.go @@ -0,0 +1,71 @@ +package linkpearl + +import ( + "context" + + "maunium.net/go/mautrix/id" +) + +// reactionPrefix is the prefix for all reaction in account data +const reactionPrefix = "cc.etke.linkpearl.reaction." + +// SendReaction sends a reaction to a message +func (l *Linkpearl) SendReaction(ctx context.Context, roomID id.RoomID, eventID id.EventID, reaction string) error { + // Check if the reaction already exists + if l.getReactionAD(ctx, roomID, eventID, reaction) != "" { + return nil + } + + resp, err := l.GetClient().SendReaction(ctx, roomID, eventID, reaction) + if err != nil { + return err + } + return l.updateReactionsAD(ctx, roomID, eventID, reaction, resp.EventID) +} + +// RedactReaction redacts a reaction from a message +func (l *Linkpearl) RedactReaction(ctx context.Context, roomID id.RoomID, eventID id.EventID, reaction string) error { + existingID := l.getReactionAD(ctx, roomID, eventID, reaction) + // Check if the reaction already exists + if existingID == "" { + return nil + } + if _, err := l.GetClient().RedactEvent(ctx, roomID, id.EventID(existingID)); err != nil { + return err + } + + return l.updateReactionsAD(ctx, roomID, eventID, reaction, "") +} + +// ReplaceReaction replaces a reaction with another +func (l *Linkpearl) ReplaceReaction(ctx context.Context, roomID id.RoomID, eventID id.EventID, oldReaction, newReaction string) error { + if err := l.RedactReaction(ctx, roomID, eventID, oldReaction); err != nil { + return err + } + return l.SendReaction(ctx, roomID, eventID, newReaction) +} + +func (l *Linkpearl) getReactionAD(ctx context.Context, roomID id.RoomID, eventID id.EventID, reaction string) string { + adID := reactionPrefix + eventID.String() + existing, err := l.GetRoomAccountData(ctx, roomID, adID) + if err != nil { + l.log.Error().Err(err).Msg("failed to get existing reactions") + return "" + } + return existing[reaction] +} + +func (l *Linkpearl) updateReactionsAD(ctx context.Context, roomID id.RoomID, eventID id.EventID, reaction string, reactionID id.EventID) error { + adID := reactionPrefix + eventID.String() + existing, err := l.GetRoomAccountData(ctx, roomID, adID) + if err != nil { + return err + } + + if reactionID == "" { + delete(existing, reaction) + } else { + existing[reaction] = reactionID.String() + } + return l.SetRoomAccountData(ctx, roomID, adID, existing) +} diff --git a/vendor/gitlab.com/etke.cc/linkpearl/send.go b/vendor/gitlab.com/etke.cc/linkpearl/send.go index 82f687a..2b40220 100644 --- a/vendor/gitlab.com/etke.cc/linkpearl/send.go +++ b/vendor/gitlab.com/etke.cc/linkpearl/send.go @@ -2,6 +2,7 @@ package linkpearl import ( "context" + "time" "maunium.net/go/mautrix" "maunium.net/go/mautrix/event" @@ -12,7 +13,7 @@ import ( // Send a message to the roomID and automatically try to encrypt it, if the destination room is encrypted // //nolint:unparam // it's public interface -func (l *Linkpearl) Send(ctx context.Context, roomID id.RoomID, content interface{}) (id.EventID, error) { +func (l *Linkpearl) Send(ctx context.Context, roomID id.RoomID, content any) (id.EventID, error) { l.log.Debug().Str("roomID", roomID.String()).Any("content", content).Msg("sending event") resp, err := l.api.SendMessageEvent(ctx, roomID, event.EventMessage, content) if err != nil { @@ -21,6 +22,18 @@ func (l *Linkpearl) Send(ctx context.Context, roomID id.RoomID, content interfac return resp.EventID, nil } +// SendTyping notification +func (l *Linkpearl) SendTyping(ctx context.Context, roomID id.RoomID, typing bool, timeout ...int) { + ttl := DefaultTypingTimeout + if len(timeout) > 0 { + ttl = timeout[0] + } + _, err := l.api.UserTyping(ctx, roomID, typing, time.Duration(ttl)*time.Second) + if err != nil { + l.log.Warn().Err(err).Bool("typing", typing).Msg("cannot set typing") + } +} + // SendNotice to a room with optional relations, markdown supported func (l *Linkpearl) SendNotice(ctx context.Context, roomID id.RoomID, message string, relates ...*event.RelatesTo) { var withRelatesTo bool diff --git a/vendor/gitlab.com/etke.cc/linkpearl/sync.go b/vendor/gitlab.com/etke.cc/linkpearl/sync.go index 88c3d22..fc28087 100644 --- a/vendor/gitlab.com/etke.cc/linkpearl/sync.go +++ b/vendor/gitlab.com/etke.cc/linkpearl/sync.go @@ -13,27 +13,27 @@ import ( // OnEventType allows callers to be notified when there are new events for the given event type. // There are no duplicate checks. func (l *Linkpearl) OnEventType(eventType event.Type, callback mautrix.EventHandler) { - l.api.Syncer.(mautrix.ExtensibleSyncer).OnEventType(eventType, callback) + l.api.Syncer.(mautrix.ExtensibleSyncer).OnEventType(eventType, callback) //nolint:forcetypeassert // we know it's an ExtensibleSyncer } // OnSync shortcut to mautrix.DefaultSyncer.OnSync func (l *Linkpearl) OnSync(callback mautrix.SyncHandler) { - l.api.Syncer.(mautrix.ExtensibleSyncer).OnSync(callback) + l.api.Syncer.(mautrix.ExtensibleSyncer).OnSync(callback) //nolint:forcetypeassert // we know it's an ExtensibleSyncer } // OnEvent shortcut to mautrix.DefaultSyncer.OnEvent func (l *Linkpearl) OnEvent(callback mautrix.EventHandler) { - l.api.Syncer.(mautrix.ExtensibleSyncer).OnEvent(callback) + l.api.Syncer.(mautrix.ExtensibleSyncer).OnEvent(callback) //nolint:forcetypeassert // we know it's an ExtensibleSyncer } func (l *Linkpearl) initSync() { - l.api.Syncer.(mautrix.ExtensibleSyncer).OnEventType( + l.api.Syncer.(mautrix.ExtensibleSyncer).OnEventType( //nolint:forcetypeassert // we know it's an ExtensibleSyncer event.StateEncryption, func(ctx context.Context, evt *event.Event) { go l.onEncryption(ctx, evt) }, ) - l.api.Syncer.(mautrix.ExtensibleSyncer).OnEventType( + l.api.Syncer.(mautrix.ExtensibleSyncer).OnEventType( //nolint:forcetypeassert // we know it's an ExtensibleSyncer event.StateMember, func(ctx context.Context, evt *event.Event) { go l.onMembership(ctx, evt) diff --git a/vendor/gitlab.com/etke.cc/linkpearl/utils.go b/vendor/gitlab.com/etke.cc/linkpearl/utils.go index 4387035..e777af9 100644 --- a/vendor/gitlab.com/etke.cc/linkpearl/utils.go +++ b/vendor/gitlab.com/etke.cc/linkpearl/utils.go @@ -1,6 +1,8 @@ package linkpearl import ( + "errors" + "github.com/rs/zerolog" "maunium.net/go/mautrix" "maunium.net/go/mautrix/event" @@ -120,26 +122,12 @@ func ParseContent(evt *event.Event, log *zerolog.Logger) { // UnwrapError tries to unwrap a error into something meaningful, like mautrix.HTTPError or mautrix.RespError func UnwrapError(err error) error { - switch err.(type) { - case nil: - return nil - case mautrix.HTTPError: - return unwrapHTTPError(err) - default: - return err + var httpErr mautrix.HTTPError + if errors.As(err, &httpErr) { + uwerr := httpErr.Unwrap() + if uwerr != nil { + return uwerr + } } -} - -func unwrapHTTPError(err error) error { - httperr, ok := err.(mautrix.HTTPError) - if !ok { - return err - } - - uwerr := httperr.Unwrap() - if uwerr != nil { - return uwerr - } - - return httperr + return err } diff --git a/vendor/go.mau.fi/util/dbutil/massinsert.go b/vendor/go.mau.fi/util/dbutil/massinsert.go new file mode 100644 index 0000000..aa84526 --- /dev/null +++ b/vendor/go.mau.fi/util/dbutil/massinsert.go @@ -0,0 +1,162 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package dbutil + +import ( + "fmt" + "regexp" + "strings" +) + +// Array is an interface for small fixed-size arrays. +// It exists because generics can't specify array sizes: https://github.com/golang/go/issues/44253 +type Array interface { + [1]any | [2]any | [3]any | [4]any | [5]any | [6]any | [7]any | [8]any | [9]any | [10]any | [11]any | [12]any | [13]any | [14]any | [15]any | [16]any | [17]any | [18]any | [19]any | [20]any +} + +// MassInsertable represents a struct that contains dynamic values for a mass insert query. +type MassInsertable[T Array] interface { + GetMassInsertValues() T +} + +// MassInsertBuilder contains pre-validated templates for building mass insert SQL queries. +type MassInsertBuilder[Item MassInsertable[DynamicParams], StaticParams Array, DynamicParams Array] struct { + queryTemplate string + placeholderTemplate string +} + +// NewMassInsertBuilder creates a new MassInsertBuilder that can build mass insert database queries. +// +// Parameters in mass insert queries are split into two types: static parameters +// and dynamic parameters. Static parameters are the same for all items being +// inserted, while dynamic parameters are different for each item. +// +// The given query should be a normal INSERT query for a single row. It can also +// have ON CONFLICT clauses, as long as the clause uses `excluded` instead of +// positional parameters. +// +// The placeholder template is used to replace the `VALUES` part of the given +// query. It should contain a positional placeholder ($1, $2, ...) for each +// static placeholder, and a fmt directive (`$%d`) for each dynamic placeholder. +// +// The given query and placeholder template are validated here and the function +// will panic if they're invalid (e.g. if the `VALUES` part of the insert query +// can't be found, or if the placeholder template doesn't have the right things). +// The idea is to use this function to populate a global variable with the mass +// insert builder, so the panic will happen at startup if the query or +// placeholder template are invalid (instead of returning an error when trying +// to use the query later). +// +// Example: +// +// type Message struct { +// ChatID int +// RemoteID string +// MXID id.EventID +// Timestamp time.Time +// } +// +// func (msg *Message) GetMassInsertValues() [3]any { +// return [3]any{msg.RemoteID, msg.MXID, msg.Timestamp.UnixMilli()} +// } +// +// const insertMessageQuery = `INSERT INTO message (chat_id, remote_id, mxid, timestamp) VALUES ($1, $2, $3, $4)` +// var massInsertMessageBuilder = dbutil.NewMassInsertBuilder[Message, [2]any](insertMessageQuery, "($1, $%d, $%d, $%d, $%d)") +// +// func DoMassInsert(ctx context.Context, messages []*Message) error { +// query, params := massInsertMessageBuilder.Build([1]any{messages[0].ChatID}, messages) +// return db.Exec(ctx, query, params...) +// } +func NewMassInsertBuilder[Item MassInsertable[DynamicParams], StaticParams Array, DynamicParams Array]( + singleInsertQuery, placeholderTemplate string, +) *MassInsertBuilder[Item, StaticParams, DynamicParams] { + var dyn DynamicParams + var stat StaticParams + totalParams := len(dyn) + len(stat) + mainQueryVariablePlaceholderParts := make([]string, totalParams) + for i := 0; i < totalParams; i++ { + mainQueryVariablePlaceholderParts[i] = fmt.Sprintf(`\$%d`, i+1) + } + mainQueryVariablePlaceholderRegex := regexp.MustCompile(fmt.Sprintf(`\(\s*%s\s*\)`, strings.Join(mainQueryVariablePlaceholderParts, `\s*,\s*`))) + queryPlaceholders := mainQueryVariablePlaceholderRegex.FindAllString(singleInsertQuery, -1) + if len(queryPlaceholders) == 0 { + panic(fmt.Errorf("invalid insert query: placeholders not found")) + } else if len(queryPlaceholders) > 1 { + panic(fmt.Errorf("invalid insert query: multiple placeholders found")) + } + for i := 0; i < len(stat); i++ { + if !strings.Contains(placeholderTemplate, fmt.Sprintf("$%d", i+1)) { + panic(fmt.Errorf("invalid placeholder template: static placeholder $%d not found", i+1)) + } + } + if strings.Contains(placeholderTemplate, fmt.Sprintf("$%d", len(stat)+1)) { + panic(fmt.Errorf("invalid placeholder template: non-static placeholder $%d found", len(stat)+1)) + } + fmtParams := make([]any, len(dyn)) + for i := 0; i < len(dyn); i++ { + fmtParams[i] = fmt.Sprintf("$%d", len(stat)+i+1) + } + formattedPlaceholder := fmt.Sprintf(placeholderTemplate, fmtParams...) + if strings.Contains(formattedPlaceholder, "!(EXTRA string=") { + panic(fmt.Errorf("invalid placeholder template: extra string found")) + } + for i := 0; i < len(dyn); i++ { + if !strings.Contains(formattedPlaceholder, fmt.Sprintf("$%d", len(stat)+i+1)) { + panic(fmt.Errorf("invalid placeholder template: dynamic placeholder $%d not found", len(stat)+i+1)) + } + } + return &MassInsertBuilder[Item, StaticParams, DynamicParams]{ + queryTemplate: strings.Replace(singleInsertQuery, queryPlaceholders[0], "%s", 1), + placeholderTemplate: placeholderTemplate, + } +} + +// Build constructs a ready-to-use mass insert SQL query using the prepared templates in this builder. +// +// This method always only produces one query. If there are lots of items, +// chunking them beforehand may be required to avoid query parameter limits. +// For example, SQLite (3.32+) has a limit of 32766 parameters by default, +// while Postgres allows up to 65535. To find out if there are too many items, +// divide the maximum number of parameters by the number of dynamic columns in +// your data and subtract the number of static columns. +// +// Example of chunking input data: +// +// var mib dbutil.MassInsertBuilder +// var db *dbutil.Database +// func MassInsert(ctx context.Context, ..., data []T) error { +// return db.DoTxn(ctx, nil, func(ctx context.Context) error { +// for _, chunk := range exslices.Chunk(data, 100) { +// query, params := mib.Build(staticParams) +// _, err := db.Exec(ctx, query, params...) +// if err != nil { +// return err +// } +// } +// return nil +// } +// } +func (mib *MassInsertBuilder[Item, StaticParams, DynamicParams]) Build(static StaticParams, data []Item) (query string, params []any) { + var itemValues DynamicParams + params = make([]any, len(static)+len(itemValues)*len(data)) + placeholders := make([]string, len(data)) + for i := 0; i < len(static); i++ { + params[i] = static[i] + } + fmtParams := make([]any, len(itemValues)) + for i, item := range data { + baseIndex := len(static) + len(itemValues)*i + itemValues = item.GetMassInsertValues() + for j := 0; j < len(itemValues); j++ { + params[baseIndex+j] = itemValues[j] + fmtParams[j] = baseIndex + j + 1 + } + placeholders[i] = fmt.Sprintf(mib.placeholderTemplate, fmtParams...) + } + query = fmt.Sprintf(mib.queryTemplate, strings.Join(placeholders, ", ")) + return +} diff --git a/vendor/go.mau.fi/util/dbutil/reflectscan.go b/vendor/go.mau.fi/util/dbutil/reflectscan.go new file mode 100644 index 0000000..77dc3ac --- /dev/null +++ b/vendor/go.mau.fi/util/dbutil/reflectscan.go @@ -0,0 +1,30 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package dbutil + +import ( + "reflect" +) + +func reflectScan[T any](row Scannable) (*T, error) { + t := new(T) + val := reflect.ValueOf(t).Elem() + fields := reflect.VisibleFields(val.Type()) + scanInto := make([]any, len(fields)) + for i, field := range fields { + scanInto[i] = val.FieldByIndex(field.Index).Addr().Interface() + } + err := row.Scan(scanInto...) + return t, err +} + +// NewSimpleReflectRowIter creates a new RowIter that uses reflection to scan rows into the given type. +// +// This is a simplified implementation that always scans to all struct fields. It does not support any kind of struct tags. +func NewSimpleReflectRowIter[T any](rows Rows, err error) RowIter[*T] { + return ConvertRowFn[*T](reflectScan[T]).NewRowIter(rows, err) +} diff --git a/vendor/maunium.net/go/mautrix/CHANGELOG.md b/vendor/maunium.net/go/mautrix/CHANGELOG.md index cece994..d7d17bc 100644 --- a/vendor/maunium.net/go/mautrix/CHANGELOG.md +++ b/vendor/maunium.net/go/mautrix/CHANGELOG.md @@ -1,3 +1,20 @@ +## v0.18.1 (2024-04-16) + +* *(format)* Added a `context.Context` field to HTMLParser's Context struct. +* *(bridge)* Added support for handling join rules, knocks, invites and bans + (thanks to [@maltee1] in [#193] and [#204]). +* *(crypto)* Changed forwarded room key handling to only accept keys with a + lower first known index than the existing session if there is one. +* *(crypto)* Changed key backup restore to assume own device list is up to date + to avoid re-requesting device list for every deleted device that has signed + key backup. +* *(crypto)* Fixed memory cache not being invalidated when storing own + cross-signing keys + +[@maltee1]: https://github.com/maltee1 +[#193]: https://github.com/mautrix/go/pull/193 +[#204]: https://github.com/mautrix/go/pull/204 + ## v0.18.0 (2024-03-16) * **Breaking change *(client, bridge, appservice)*** Dropped support for diff --git a/vendor/maunium.net/go/mautrix/client.go b/vendor/maunium.net/go/mautrix/client.go index ad68e79..686c0e1 100644 --- a/vendor/maunium.net/go/mautrix/client.go +++ b/vendor/maunium.net/go/mautrix/client.go @@ -338,6 +338,7 @@ type FullRequest struct { SensitiveContent bool Handler ClientResponseHandler Logger *zerolog.Logger + Client *http.Client } var requestID int32 @@ -424,7 +425,10 @@ func (cli *Client) MakeFullRequest(ctx context.Context, params FullRequest) ([]b if len(cli.AccessToken) > 0 { req.Header.Set("Authorization", "Bearer "+cli.AccessToken) } - return cli.executeCompiledRequest(req, params.MaxAttempts-1, 4*time.Second, params.ResponseJSON, params.Handler) + if params.Client == nil { + params.Client = cli.Client + } + return cli.executeCompiledRequest(req, params.MaxAttempts-1, 4*time.Second, params.ResponseJSON, params.Handler, params.Client) } func (cli *Client) cliOrContextLog(ctx context.Context) *zerolog.Logger { @@ -435,7 +439,7 @@ func (cli *Client) cliOrContextLog(ctx context.Context) *zerolog.Logger { return log } -func (cli *Client) doRetry(req *http.Request, cause error, retries int, backoff time.Duration, responseJSON interface{}, handler ClientResponseHandler) ([]byte, error) { +func (cli *Client) doRetry(req *http.Request, cause error, retries int, backoff time.Duration, responseJSON interface{}, handler ClientResponseHandler, client *http.Client) ([]byte, error) { log := zerolog.Ctx(req.Context()) if req.Body != nil { if req.GetBody == nil { @@ -453,7 +457,7 @@ func (cli *Client) doRetry(req *http.Request, cause error, retries int, backoff Int("retry_in_seconds", int(backoff.Seconds())). Msg("Request failed, retrying") time.Sleep(backoff) - return cli.executeCompiledRequest(req, retries-1, backoff*2, responseJSON, handler) + return cli.executeCompiledRequest(req, retries-1, backoff*2, responseJSON, handler, client) } func readRequestBody(req *http.Request, res *http.Response) ([]byte, error) { @@ -535,17 +539,17 @@ func ParseErrorResponse(req *http.Request, res *http.Response) ([]byte, error) { } } -func (cli *Client) executeCompiledRequest(req *http.Request, retries int, backoff time.Duration, responseJSON interface{}, handler ClientResponseHandler) ([]byte, error) { +func (cli *Client) executeCompiledRequest(req *http.Request, retries int, backoff time.Duration, responseJSON interface{}, handler ClientResponseHandler, client *http.Client) ([]byte, error) { cli.RequestStart(req) startTime := time.Now() - res, err := cli.Client.Do(req) + res, err := client.Do(req) duration := time.Now().Sub(startTime) if res != nil { defer res.Body.Close() } if err != nil { if retries > 0 { - return cli.doRetry(req, err, retries, backoff, responseJSON, handler) + return cli.doRetry(req, err, retries, backoff, responseJSON, handler, client) } err = HTTPError{ Request: req, @@ -560,7 +564,7 @@ func (cli *Client) executeCompiledRequest(req *http.Request, retries int, backof if retries > 0 && retryafter.Should(res.StatusCode, !cli.IgnoreRateLimit) { backoff = retryafter.Parse(res.Header.Get("Retry-After"), backoff) - return cli.doRetry(req, fmt.Errorf("HTTP %d", res.StatusCode), retries, backoff, responseJSON, handler) + return cli.doRetry(req, fmt.Errorf("HTTP %d", res.StatusCode), retries, backoff, responseJSON, handler, client) } var body []byte diff --git a/vendor/maunium.net/go/mautrix/crypto/cross_sign_store.go b/vendor/maunium.net/go/mautrix/crypto/cross_sign_store.go index 28d0bad..456ab6e 100644 --- a/vendor/maunium.net/go/mautrix/crypto/cross_sign_store.go +++ b/vendor/maunium.net/go/mautrix/crypto/cross_sign_store.go @@ -96,5 +96,12 @@ func (mach *OlmMachine) storeCrossSigningKeys(ctx context.Context, crossSigningK } } } + + // Clear internal cache so that it refreshes from crypto store + if userID == mach.Client.UserID && mach.crossSigningPubkeys != nil { + log.Debug().Msg("Resetting internal cross-signing key cache") + mach.crossSigningPubkeys = nil + mach.crossSigningPubkeysFetched = false + } } } diff --git a/vendor/maunium.net/go/mautrix/crypto/cross_sign_validation.go b/vendor/maunium.net/go/mautrix/crypto/cross_sign_validation.go index ff2452e..04a179d 100644 --- a/vendor/maunium.net/go/mautrix/crypto/cross_sign_validation.go +++ b/vendor/maunium.net/go/mautrix/crypto/cross_sign_validation.go @@ -32,7 +32,7 @@ func (mach *OlmMachine) ResolveTrustContext(ctx context.Context, device *id.Devi } theirMSK, ok := theirKeys[id.XSUsageMaster] if !ok { - mach.machOrContextLog(ctx).Error(). + mach.machOrContextLog(ctx).Debug(). Str("user_id", device.UserID.String()). Msg("Master key of user not found") return id.TrustStateUnset, nil diff --git a/vendor/maunium.net/go/mautrix/crypto/keybackup.go b/vendor/maunium.net/go/mautrix/crypto/keybackup.go index d3701e9..afdb84f 100644 --- a/vendor/maunium.net/go/mautrix/crypto/keybackup.go +++ b/vendor/maunium.net/go/mautrix/crypto/keybackup.go @@ -66,8 +66,10 @@ func (mach *OlmMachine) GetAndVerifyLatestKeyBackupVersion(ctx context.Context) var key id.Ed25519 if keyName == crossSigningPubkeys.MasterKey.String() { key = crossSigningPubkeys.MasterKey - } else if device, err := mach.GetOrFetchDevice(ctx, mach.Client.UserID, id.DeviceID(keyName)); err != nil { - log.Warn().Err(err).Msg("Failed to fetch device") + } else if device, err := mach.CryptoStore.GetDevice(ctx, mach.Client.UserID, id.DeviceID(keyName)); err != nil { + return nil, fmt.Errorf("failed to get device %s/%s from store: %w", mach.Client.UserID, keyName, err) + } else if device == nil { + log.Warn().Err(err).Msg("Device does not exist, ignoring signature") continue } else if !mach.IsDeviceTrusted(device) { log.Warn().Err(err).Msg("Device is not trusted") diff --git a/vendor/maunium.net/go/mautrix/crypto/keysharing.go b/vendor/maunium.net/go/mautrix/crypto/keysharing.go index 05e7f89..d1b2e92 100644 --- a/vendor/maunium.net/go/mautrix/crypto/keysharing.go +++ b/vendor/maunium.net/go/mautrix/crypto/keysharing.go @@ -184,6 +184,11 @@ func (mach *OlmMachine) importForwardedRoomKey(ctx context.Context, evt *Decrypt MaxMessages: maxMessages, IsScheduled: content.IsScheduled, } + existingIGS, _ := mach.CryptoStore.GetGroupSession(ctx, igs.RoomID, igs.SenderKey, igs.ID()) + if existingIGS != nil && existingIGS.Internal.FirstKnownIndex() <= igs.Internal.FirstKnownIndex() { + // We already have an equivalent or better session in the store, so don't override it. + return false + } err = mach.CryptoStore.PutGroupSession(ctx, content.RoomID, content.SenderKey, content.SessionID, igs) if err != nil { log.Error().Err(err).Msg("Failed to store new inbound group session") diff --git a/vendor/maunium.net/go/mautrix/event/events.go b/vendor/maunium.net/go/mautrix/event/events.go index f7b4d4d..4653a53 100644 --- a/vendor/maunium.net/go/mautrix/event/events.go +++ b/vendor/maunium.net/go/mautrix/event/events.go @@ -149,5 +149,6 @@ type Unsigned struct { func (us *Unsigned) IsEmpty() bool { return us.PrevContent == nil && us.PrevSender == "" && us.ReplacesState == "" && us.Age == 0 && - us.TransactionID == "" && us.RedactedBecause == nil && us.InviteRoomState == nil && us.Relations == nil + us.TransactionID == "" && us.RedactedBecause == nil && us.InviteRoomState == nil && us.Relations == nil && + us.BeeperHSOrder == 0 } diff --git a/vendor/maunium.net/go/mautrix/format/htmlparser.go b/vendor/maunium.net/go/mautrix/format/htmlparser.go index eb2a662..8ddd881 100644 --- a/vendor/maunium.net/go/mautrix/format/htmlparser.go +++ b/vendor/maunium.net/go/mautrix/format/htmlparser.go @@ -7,6 +7,7 @@ package format import ( + "context" "fmt" "math" "strconv" @@ -33,14 +34,16 @@ func (ts TagStack) Has(tag string) bool { } type Context struct { + Ctx context.Context ReturnData map[string]any TagStack TagStack PreserveWhitespace bool } -func NewContext() Context { +func NewContext(ctx context.Context) Context { return Context{ + Ctx: ctx, ReturnData: map[string]any{}, TagStack: make(TagStack, 0, 4), } @@ -411,7 +414,7 @@ func HTMLToText(html string) string { Newline: "\n", HorizontalLine: "\n---\n", PillConverter: DefaultPillConverter, - }).Parse(html, NewContext()) + }).Parse(html, NewContext(context.TODO())) } // HTMLToMarkdown converts Matrix HTML into markdown with the default settings. @@ -429,5 +432,5 @@ func HTMLToMarkdown(html string) string { } return fmt.Sprintf("[%s](%s)", text, href) }, - }).Parse(html, NewContext()) + }).Parse(html, NewContext(context.TODO())) } diff --git a/vendor/maunium.net/go/mautrix/version.go b/vendor/maunium.net/go/mautrix/version.go index 82817bc..e00141a 100644 --- a/vendor/maunium.net/go/mautrix/version.go +++ b/vendor/maunium.net/go/mautrix/version.go @@ -7,7 +7,7 @@ import ( "strings" ) -const Version = "v0.18.0" +const Version = "v0.18.1" var GoModVersion = "" var Commit = "" diff --git a/vendor/modules.txt b/vendor/modules.txt index 1209364..39fada1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -17,7 +17,7 @@ github.com/emersion/go-msgauth/dkim # github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 ## explicit; go 1.12 github.com/emersion/go-sasl -# github.com/emersion/go-smtp v0.21.0 +# github.com/emersion/go-smtp v0.21.1 ## explicit; go 1.13 github.com/emersion/go-smtp # github.com/fsnotify/fsnotify v1.7.0 @@ -136,13 +136,13 @@ gitlab.com/etke.cc/go/env # gitlab.com/etke.cc/go/fswatcher v1.0.0 ## explicit; go 1.20 gitlab.com/etke.cc/go/fswatcher -# gitlab.com/etke.cc/go/healthchecks/v2 v2.0.0 +# gitlab.com/etke.cc/go/healthchecks/v2 v2.2.0 ## explicit; go 1.18 gitlab.com/etke.cc/go/healthchecks/v2 # gitlab.com/etke.cc/go/mxidwc v1.0.0 ## explicit; go 1.19 gitlab.com/etke.cc/go/mxidwc -# gitlab.com/etke.cc/go/psd v1.1.1 +# gitlab.com/etke.cc/go/psd v1.1.2 ## explicit; go 1.21.0 gitlab.com/etke.cc/go/psd # gitlab.com/etke.cc/go/secgen v1.2.0 @@ -154,10 +154,10 @@ gitlab.com/etke.cc/go/trysmtp # gitlab.com/etke.cc/go/validator v1.0.7 ## explicit; go 1.18 gitlab.com/etke.cc/go/validator -# gitlab.com/etke.cc/linkpearl v0.0.0-20240316115913-106577b88942 +# gitlab.com/etke.cc/linkpearl v0.0.0-20240425105001-435ae2720365 ## explicit; go 1.21 gitlab.com/etke.cc/linkpearl -# go.mau.fi/util v0.4.1 +# go.mau.fi/util v0.4.2 ## explicit; go 1.21 go.mau.fi/util/base58 go.mau.fi/util/dbutil @@ -182,7 +182,7 @@ golang.org/x/crypto/internal/poly1305 golang.org/x/crypto/pbkdf2 golang.org/x/crypto/ssh golang.org/x/crypto/ssh/internal/bcrypt_pbkdf -# golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 +# golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f ## explicit; go 1.20 golang.org/x/exp/constraints golang.org/x/exp/maps @@ -220,7 +220,7 @@ golang.org/x/text/language golang.org/x/text/runes golang.org/x/text/transform golang.org/x/text/unicode/norm -# maunium.net/go/mautrix v0.18.0 +# maunium.net/go/mautrix v0.18.1 ## explicit; go 1.21 maunium.net/go/mautrix maunium.net/go/mautrix/crypto