Add support for encrypted files

This commit is contained in:
Tulir Asokan 2020-04-29 02:45:54 +03:00
parent fa04323daf
commit a9dff6da73
7 changed files with 82 additions and 47 deletions

2
go.mod
View File

@ -21,7 +21,7 @@ require (
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2 gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.2.8
maunium.net/go/mautrix v0.2.0-beta.4.0.20200428185931-73915bafb538 maunium.net/go/mautrix v0.2.0-beta.4.0.20200428234424-a14b55c5445f
maunium.net/go/mauview v0.1.0 maunium.net/go/mauview v0.1.0
maunium.net/go/tcell v0.1.0 maunium.net/go/tcell v0.1.0
) )

2
go.sum
View File

@ -88,6 +88,8 @@ maunium.net/go/mautrix v0.2.0-beta.4.0.20200427215704-fe82e2b914c8 h1:xflYDdpEon
maunium.net/go/mautrix v0.2.0-beta.4.0.20200427215704-fe82e2b914c8/go.mod h1:SkGZzch8CvU2qKtNpYxtzZ0sQxfVEJ3IsVVLSUBUx9Y= maunium.net/go/mautrix v0.2.0-beta.4.0.20200427215704-fe82e2b914c8/go.mod h1:SkGZzch8CvU2qKtNpYxtzZ0sQxfVEJ3IsVVLSUBUx9Y=
maunium.net/go/mautrix v0.2.0-beta.4.0.20200428185931-73915bafb538 h1:E/6URkgRmxhNid8mA+PV25Bi8F/4Yg5nmsxIi/MWDrU= maunium.net/go/mautrix v0.2.0-beta.4.0.20200428185931-73915bafb538 h1:E/6URkgRmxhNid8mA+PV25Bi8F/4Yg5nmsxIi/MWDrU=
maunium.net/go/mautrix v0.2.0-beta.4.0.20200428185931-73915bafb538/go.mod h1:SkGZzch8CvU2qKtNpYxtzZ0sQxfVEJ3IsVVLSUBUx9Y= maunium.net/go/mautrix v0.2.0-beta.4.0.20200428185931-73915bafb538/go.mod h1:SkGZzch8CvU2qKtNpYxtzZ0sQxfVEJ3IsVVLSUBUx9Y=
maunium.net/go/mautrix v0.2.0-beta.4.0.20200428234424-a14b55c5445f h1:kYrIUjr2v6fuVZfs5QnzRJ2YzbZVH21aP77a5ynFHNk=
maunium.net/go/mautrix v0.2.0-beta.4.0.20200428234424-a14b55c5445f/go.mod h1:SkGZzch8CvU2qKtNpYxtzZ0sQxfVEJ3IsVVLSUBUx9Y=
maunium.net/go/mauview v0.1.0 h1:x2WdkKI2zdriJuPAB0CKlwmnHGE7W9xfM5z6RgG+IIg= maunium.net/go/mauview v0.1.0 h1:x2WdkKI2zdriJuPAB0CKlwmnHGE7W9xfM5z6RgG+IIg=
maunium.net/go/mauview v0.1.0/go.mod h1:og9WbzmWe9SNYNyOFlCv8qa9zMcOvG2nzRJ5vYyud9U= maunium.net/go/mauview v0.1.0/go.mod h1:og9WbzmWe9SNYNyOFlCv8qa9zMcOvG2nzRJ5vYyud9U=
maunium.net/go/tcell v0.1.0 h1:XzsEoGCvOw5nac+tlkSLzQcliLYTN4PrtA7ar2ptjSM= maunium.net/go/tcell v0.1.0 h1:XzsEoGCvOw5nac+tlkSLzQcliLYTN4PrtA7ar2ptjSM=

View File

@ -18,6 +18,7 @@ package ifc
import ( import (
"maunium.net/go/mautrix" "maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto/attachment"
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
@ -59,8 +60,8 @@ type MatrixContainer interface {
GetRoom(roomID id.RoomID) *rooms.Room GetRoom(roomID id.RoomID) *rooms.Room
GetOrCreateRoom(roomID id.RoomID) *rooms.Room GetOrCreateRoom(roomID id.RoomID) *rooms.Room
Download(uri id.ContentURI) ([]byte, error) Download(uri id.ContentURI, file *attachment.EncryptedFile) ([]byte, error)
DownloadToDisk(uri id.ContentURI, target string) (string, error) DownloadToDisk(uri id.ContentURI, file *attachment.EncryptedFile, target string) (string, error)
GetDownloadURL(uri id.ContentURI) string GetDownloadURL(uri id.ContentURI) string
GetCachePath(uri id.ContentURI) string GetCachePath(uri id.ContentURI) string
} }

View File

@ -17,7 +17,6 @@
package matrix package matrix
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/gob" "encoding/gob"
@ -38,6 +37,7 @@ import (
"maunium.net/go/mautrix" "maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto" "maunium.net/go/mautrix/crypto"
"maunium.net/go/mautrix/crypto/attachment"
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format" "maunium.net/go/mautrix/format"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
@ -1061,7 +1061,7 @@ func cp(src, dst string) error {
return out.Close() return out.Close()
} }
func (c *Container) DownloadToDisk(uri id.ContentURI, target string) (fullPath string, err error) { func (c *Container) DownloadToDisk(uri id.ContentURI, file *attachment.EncryptedFile, target string) (fullPath string, err error) {
cachePath := c.GetCachePath(uri) cachePath := c.GetCachePath(uri)
if target == "" { if target == "" {
fullPath = cachePath fullPath = cachePath
@ -1072,21 +1072,27 @@ func (c *Container) DownloadToDisk(uri id.ContentURI, target string) (fullPath s
} }
if _, statErr := os.Stat(cachePath); os.IsNotExist(statErr) { if _, statErr := os.Stat(cachePath); os.IsNotExist(statErr) {
var file *os.File
file, err = os.OpenFile(cachePath, os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return
}
defer file.Close()
var body io.ReadCloser var body io.ReadCloser
body, err = c.client.Download(uri) body, err = c.client.Download(uri)
if err != nil { if err != nil {
return return
} }
defer body.Close()
_, err = io.Copy(file, body) var data []byte
data, err = ioutil.ReadAll(body)
_ = body.Close()
if err != nil {
return
}
if file != nil {
data, err = file.Decrypt(data)
if err != nil {
return
}
}
err = ioutil.WriteFile(cachePath, data, 0600)
if err != nil { if err != nil {
return return
} }
@ -1106,7 +1112,7 @@ func (c *Container) DownloadToDisk(uri id.ContentURI, target string) (fullPath s
// Download fetches the given Matrix content (mxc) URL and returns the data, homeserver, file ID and potential errors. // Download fetches the given Matrix content (mxc) URL and returns the data, homeserver, file ID and potential errors.
// //
// The file will be either read from the media cache (if found) or downloaded from the server. // The file will be either read from the media cache (if found) or downloaded from the server.
func (c *Container) Download(uri id.ContentURI) (data []byte, err error) { func (c *Container) Download(uri id.ContentURI, file *attachment.EncryptedFile) (data []byte, err error) {
cacheFile := c.GetCachePath(uri) cacheFile := c.GetCachePath(uri)
var info os.FileInfo var info os.FileInfo
if info, err = os.Stat(cacheFile); err == nil && !info.IsDir() { if info, err = os.Stat(cacheFile); err == nil && !info.IsDir() {
@ -1116,7 +1122,7 @@ func (c *Container) Download(uri id.ContentURI) (data []byte, err error) {
} }
} }
data, err = c.download(uri, cacheFile) data, err = c.download(uri, file, cacheFile)
return return
} }
@ -1124,21 +1130,25 @@ func (c *Container) GetDownloadURL(uri id.ContentURI) string {
return c.client.GetDownloadURL(uri) return c.client.GetDownloadURL(uri)
} }
func (c *Container) download(uri id.ContentURI, cacheFile string) (data []byte, err error) { func (c *Container) download(uri id.ContentURI, file *attachment.EncryptedFile, cacheFile string) (data []byte, err error) {
var body io.ReadCloser var body io.ReadCloser
body, err = c.client.Download(uri) body, err = c.client.Download(uri)
if err != nil { if err != nil {
return return
} }
defer body.Close()
var buf bytes.Buffer data, err = ioutil.ReadAll(body)
_, err = io.Copy(&buf, body) _ = body.Close()
if err != nil { if err != nil {
return return
} }
data = buf.Bytes() if file != nil {
data, err = file.Decrypt(data)
if err != nil {
return
}
}
err = ioutil.WriteFile(cacheFile, data, 0600) err = ioutil.WriteFile(cacheFile, data, 0600)
return return

View File

@ -22,7 +22,7 @@ import (
"image" "image"
"image/color" "image/color"
"maunium.net/go/gomuks/matrix/muksevt" "maunium.net/go/mautrix/crypto/attachment"
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
"maunium.net/go/mauview" "maunium.net/go/mauview"
@ -32,14 +32,19 @@ import (
"maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface" "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/lib/ansimage" "maunium.net/go/gomuks/lib/ansimage"
"maunium.net/go/gomuks/matrix/muksevt"
"maunium.net/go/gomuks/ui/messages/tstring" "maunium.net/go/gomuks/ui/messages/tstring"
) )
type FileMessage struct { type FileMessage struct {
Type event.MessageType Type event.MessageType
Body string Body string
URL id.ContentURI
Thumbnail id.ContentURI URL id.ContentURI
File *attachment.EncryptedFile
Thumbnail id.ContentURI
ThumbnailFile *attachment.EncryptedFile
imageData []byte imageData []byte
buffer []tstring.TString buffer []tstring.TString
@ -49,14 +54,23 @@ type FileMessage struct {
// NewFileMessage creates a new FileMessage object with the provided values and the default state. // NewFileMessage creates a new FileMessage object with the provided values and the default state.
func NewFileMessage(matrix ifc.MatrixContainer, evt *muksevt.Event, displayname string) *UIMessage { func NewFileMessage(matrix ifc.MatrixContainer, evt *muksevt.Event, displayname string) *UIMessage {
content := evt.Content.AsMessage() content := evt.Content.AsMessage()
url, _ := content.URL.Parse() var file, thumbnailFile *attachment.EncryptedFile
thumbnail, _ := content.GetInfo().ThumbnailURL.Parse() if content.File != nil {
file = &content.File.EncryptedFile
content.URL = content.File.URL
}
if content.GetInfo().ThumbnailFile != nil {
thumbnailFile = &content.Info.ThumbnailFile.EncryptedFile
content.Info.ThumbnailURL = content.Info.ThumbnailFile.URL
}
return newUIMessage(evt, displayname, &FileMessage{ return newUIMessage(evt, displayname, &FileMessage{
Type: content.MsgType, Type: content.MsgType,
Body: content.Body, Body: content.Body,
URL: url, URL: content.URL,
Thumbnail: thumbnail, File: file,
matrix: matrix, Thumbnail: content.GetInfo().ThumbnailURL,
ThumbnailFile: thumbnailFile,
matrix: matrix,
}) })
} }
@ -96,17 +110,20 @@ func (msg *FileMessage) String() string {
} }
func (msg *FileMessage) DownloadPreview() { func (msg *FileMessage) DownloadPreview() {
url := msg.Thumbnail var url id.ContentURI
if url.IsEmpty() { var file *attachment.EncryptedFile
if msg.Type == event.MsgImage && !msg.URL.IsEmpty() { if !msg.Thumbnail.IsEmpty() {
msg.Thumbnail = msg.URL url = msg.Thumbnail
url = msg.Thumbnail file = msg.ThumbnailFile
} else { } else if msg.Type == event.MsgImage && !msg.URL.IsEmpty() {
return msg.Thumbnail = msg.URL
} url = msg.URL
file = msg.File
} else {
return
} }
debug.Print("Loading file:", url) debug.Print("Loading file:", url)
data, err := msg.matrix.Download(url) data, err := msg.matrix.Download(url, file)
if err != nil { if err != nil {
debug.Printf("Failed to download file %s: %v", url, err) debug.Printf("Failed to download file %s: %v", url, err)
return return

View File

@ -20,13 +20,13 @@ import (
"fmt" "fmt"
"strings" "strings"
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/matrix/muksevt"
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
"maunium.net/go/tcell" "maunium.net/go/tcell"
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface" "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix/muksevt"
"maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/matrix/rooms"
"maunium.net/go/gomuks/ui/messages/html" "maunium.net/go/gomuks/ui/messages/html"
"maunium.net/go/gomuks/ui/messages/tstring" "maunium.net/go/gomuks/ui/messages/tstring"

View File

@ -26,6 +26,7 @@ import (
"github.com/kyokomi/emoji" "github.com/kyokomi/emoji"
"github.com/mattn/go-runewidth" "github.com/mattn/go-runewidth"
"maunium.net/go/mautrix/crypto/attachment"
"maunium.net/go/mauview" "maunium.net/go/mauview"
"maunium.net/go/tcell" "maunium.net/go/tcell"
@ -122,6 +123,10 @@ func NewRoomView(parent *MainView, room *rooms.Room) *RoomView {
SetPressKeyUpAtStartFunc(view.EditPrevious). SetPressKeyUpAtStartFunc(view.EditPrevious).
SetPressKeyDownAtEndFunc(view.EditNext) SetPressKeyDownAtEndFunc(view.EditNext)
if room.Encrypted {
view.input.SetPlaceholder("Send an encrypted message...")
}
view.topic. view.topic.
SetTextColor(tcell.ColorWhite). SetTextColor(tcell.ColorWhite).
SetBackgroundColor(tcell.ColorDarkGreen) SetBackgroundColor(tcell.ColorDarkGreen)
@ -202,7 +207,7 @@ func (view *RoomView) OnSelect(message *messages.UIMessage) {
} else if view.selectReason == SelectDownload { } else if view.selectReason == SelectDownload {
path = msg.Body path = msg.Body
} }
go view.Download(msg.URL, path, view.selectReason == SelectOpen) go view.Download(msg.URL, msg.File, path, view.selectReason == SelectOpen)
} }
} }
view.selecting = false view.selecting = false
@ -624,8 +629,8 @@ func (view *RoomView) InputSubmit(text string) {
view.SetInputText("") view.SetInputText("")
} }
func (view *RoomView) Download(url id.ContentURI, filename string, openFile bool) { func (view *RoomView) Download(url id.ContentURI, file *attachment.EncryptedFile, filename string, openFile bool) {
path, err := view.parent.matrix.DownloadToDisk(url, filename) path, err := view.parent.matrix.DownloadToDisk(url, file, filename)
if err != nil { if err != nil {
view.AddServiceMessage(fmt.Sprintf("Failed to download media: %v", err)) view.AddServiceMessage(fmt.Sprintf("Failed to download media: %v", err))
view.parent.parent.Render() view.parent.parent.Render()