Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 70 additions & 19 deletions bridge/matrix/matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package bmatrix
import (
"bytes"
"fmt"
"io"
"mime"
"net/http"
"regexp"
"strings"
"sync"
Expand All @@ -12,8 +14,13 @@ import (
"github.com/matterbridge-org/matterbridge/bridge"
"github.com/matterbridge-org/matterbridge/bridge/config"
"github.com/matterbridge-org/matterbridge/bridge/helper"
// Custom fork of unmaintained library, needs replacement:
"image"
// Initialize specific format decoders,
// see https://pkg.go.dev/image
matrix "github.com/matterbridge/gomatrix"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
)

var (
Expand Down Expand Up @@ -393,6 +400,17 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
return resp.EventID, err
}

func (b *Bmatrix) NewHttpRequest(method, uri string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequest(method, uri, body)
if err != nil {
return nil, err
}

req.Header.Add("Authorization", "Bearer "+b.mc.AccessToken)

return req, nil
}

func (b *Bmatrix) handlematrix() {
syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
syncer.OnEventType("m.room.redaction", b.handleEvent)
Expand Down Expand Up @@ -472,6 +490,25 @@ func (b *Bmatrix) handleReply(ev *matrix.Event, rmsg config.Message) bool {
return true
}

func (b *Bmatrix) handleAttachment(ev *matrix.Event, rmsg config.Message) bool {
if !b.containsAttachment(ev.Content) {
return false
}

go func() {
// File download is processed in the background to avoid stalling
err := b.handleDownloadFile(&rmsg, ev.Content)
if err != nil {
b.Log.Errorf("%#v", err)
return
}

b.Remote <- rmsg
}()

return true
}

func (b *Bmatrix) handleMemberChange(ev *matrix.Event) {
// Update the displayname on join messages, according to https://matrix.org/docs/spec/client_server/r0.6.1#events-on-change-of-profile-information
if ev.Content["membership"] == "join" {
Expand Down Expand Up @@ -539,12 +576,10 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {
return
}

// Do we have attachments
if b.containsAttachment(ev.Content) {
err := b.handleDownloadFile(&rmsg, ev.Content)
if err != nil {
b.Log.Errorf("download failed: %#v", err)
}
// Do we have an attachment
// TODO: does matrix support multiple attachments?
if b.handleAttachment(ev, rmsg) {
return
}

b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account)
Expand All @@ -570,7 +605,10 @@ func (b *Bmatrix) handleDownloadFile(rmsg *config.Message, content map[string]in
if url, ok = content["url"].(string); !ok {
return fmt.Errorf("url isn't a %T", url)
}
url = strings.ReplaceAll(url, "mxc://", b.GetString("Server")+"/_matrix/media/v1/download/")
// Matrix downloads now have to be authenticated with an access token
// See https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3916-authentication-for-media.md
// Also see: https://github.com/matterbridge-org/matterbridge/issues/36
url = strings.ReplaceAll(url, "mxc://", b.GetString("Server")+"/_matrix/client/v1/media/download/")

if info, ok = content["info"].(map[string]interface{}); !ok {
return fmt.Errorf("info isn't a %T", info)
Expand Down Expand Up @@ -601,18 +639,11 @@ func (b *Bmatrix) handleDownloadFile(rmsg *config.Message, content map[string]in
}
}

// check if the size is ok
err := helper.HandleDownloadSize(b.Log, rmsg, name, int64(size), b.General)
// TODO: add attachment ID?
err := b.AddAttachmentFromURL(rmsg, name, "", "", url)
if err != nil {
return err
}
// actually download the file
data, err := helper.DownloadFile(url)
if err != nil {
return fmt.Errorf("download %s failed %#v", url, err)
}
// add the downloaded data to the message
helper.HandleDownloadData(b.Log, rmsg, name, "", url, data, b.General)
return nil
}

Expand Down Expand Up @@ -670,9 +701,29 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf
}
case strings.Contains(mtype, "image"):
b.Log.Debugf("sendImage %s", res.ContentURI)
err = b.retry(func() error {
_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI)

cfg, format, err2 := image.DecodeConfig(bytes.NewReader(*fi.Data))
if err2 != nil {
b.Log.WithError(err2).Errorf("Failed to decode image %s", fi.Name)
return
}

b.Log.Debugf("Image format detected: %s (%dx%d)", format, cfg.Width, cfg.Height)

img := matrix.ImageMessage{
MsgType: "m.image",
Body: fi.Name,
URL: res.ContentURI,
Info: matrix.ImageInfo{
Mimetype: mtype,
Size: uint(len(*fi.Data)),
Width: uint(cfg.Width), // #nosec G115 -- go std will not returned negative size
Height: uint(cfg.Height), // #nosec G115 -- go std will not returned negative size
},
}

err = b.retry(func() error {
_, err = b.mc.SendMessageEvent(channel, "m.room.message", img)
return err
})
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
- general
- when downloading a file attachment from a remote HTTP server, matterbridge will now error if
the return code is not 200 to avoid saving trash data ([#20](https://github.com/matterbridge-org/matterbridge/pull/20))
- matrix
- attachments received from matrix are working again, with authenticated media (MSC3916) implemented ([#61](https://github.com/matterbridge-org/matterbridge/pull/61))
- image attachments are now send as images with more metadata ([#61](https://github.com/matterbridge-org/matterbridge/pull/61))

## Upstream

Expand Down
Loading