Эх сурвалжийг харах

Add support for encrypting preview image

Tulir Asokan 3 жил өмнө
parent
commit
9fee8a50a4
3 өөрчлөгдсөн 58 нэмэгдсэн , 33 устгасан
  1. 1 1
      go.mod
  2. 2 2
      go.sum
  3. 55 30
      urlpreview.go

+ 1 - 1
go.mod

@@ -10,7 +10,7 @@ require (
 	github.com/prometheus/client_golang v1.11.0
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/tidwall/gjson v1.13.0
-	go.mau.fi/whatsmeow v0.0.0-20220204175019-e490de34933c
+	go.mau.fi/whatsmeow v0.0.0-20220204210537-a425ddb0b16c
 	golang.org/x/image v0.0.0-20211028202545-6944b10bf410
 	google.golang.org/protobuf v1.27.1
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b

+ 2 - 2
go.sum

@@ -140,8 +140,8 @@ github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
 github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
 go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910 h1:9FFhG0OmkuMau5UEaTgiUQ+7cSbtbOQ7hiWKdN8OI3I=
 go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910/go.mod h1:AufGrvVh+00Nc07Jm4hTquh7yleZyn20tKJI2wCPAKg=
-go.mau.fi/whatsmeow v0.0.0-20220204175019-e490de34933c h1:AVSYHQ0N5n3buL+thypCk2jiltD+3+pUQ7oPVhC7I3w=
-go.mau.fi/whatsmeow v0.0.0-20220204175019-e490de34933c/go.mod h1:8jUjOAi3xtGubxcZgG8uSHpAdyQXBRbWAfxkctX/4y4=
+go.mau.fi/whatsmeow v0.0.0-20220204210537-a425ddb0b16c h1:hwuZ1W55J2uSwm029dREAr6crSVa+i5VsF91ltK389k=
+go.mau.fi/whatsmeow v0.0.0-20220204210537-a425ddb0b16c/go.mod h1:8jUjOAi3xtGubxcZgG8uSHpAdyQXBRbWAfxkctX/4y4=
 golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

+ 55 - 30
urlpreview.go

@@ -22,10 +22,13 @@ import (
 	"encoding/json"
 	"image"
 	"net/http"
+	"strings"
+	"time"
 
 	"github.com/tidwall/gjson"
 	"google.golang.org/protobuf/proto"
 	"maunium.net/go/mautrix/appservice"
+	"maunium.net/go/mautrix/crypto/attachment"
 	"maunium.net/go/mautrix/event"
 	"maunium.net/go/mautrix/id"
 
@@ -34,16 +37,19 @@ import (
 )
 
 type BeeperLinkPreview struct {
-	MatchedURL   string              `json:"matched_url"`
-	CanonicalURL string              `json:"og:url,omitempty"`
-	Title        string              `json:"og:title,omitempty"`
-	Type         string              `json:"og:type,omitempty"`
-	Description  string              `json:"og:description,omitempty"`
-	Image        id.ContentURIString `json:"og:image,omitempty"`
-	ImageWidth   int                 `json:"og:image:width,omitempty"`
-	ImageHeight  int                 `json:"og:image:height,omitempty"`
+	MatchedURL   string `json:"matched_url"`
+	CanonicalURL string `json:"og:url,omitempty"`
+	Title        string `json:"og:title,omitempty"`
+	Type         string `json:"og:type,omitempty"`
+	Description  string `json:"og:description,omitempty"`
 
-	MatchedURLFallback string `json:"matchedUrl"`
+	ImageURL        id.ContentURIString      `json:"og:image,omitempty"`
+	ImageEncryption *event.EncryptedFileInfo `json:"beeper:image:encryption,omitempty"`
+
+	ImageSize   int    `json:"matrix:image:size,omitempty"`
+	ImageWidth  int    `json:"og:image:width,omitempty"`
+	ImageHeight int    `json:"og:image:height,omitempty"`
+	ImageType   string `json:"og:image:type,omitempty"`
 }
 
 func (portal *Portal) convertURLPreviewToBeeper(intent *appservice.IntentAPI, source *User, msg *waProto.ExtendedTextMessage) (output *BeeperLinkPreview) {
@@ -57,7 +63,6 @@ func (portal *Portal) convertURLPreviewToBeeper(intent *appservice.IntentAPI, so
 		Title:        msg.GetTitle(),
 		Description:  msg.GetDescription(),
 	}
-	output.MatchedURLFallback = output.MatchedURL
 	if len(output.CanonicalURL) == 0 {
 		output.CanonicalURL = output.MatchedURL
 	}
@@ -73,19 +78,32 @@ func (portal *Portal) convertURLPreviewToBeeper(intent *appservice.IntentAPI, so
 		thumbnailData = msg.JpegThumbnail
 	}
 	if thumbnailData != nil {
-		mxc, err := intent.UploadBytes(thumbnailData, http.DetectContentType(thumbnailData))
+		output.ImageHeight = int(msg.GetThumbnailHeight())
+		output.ImageWidth = int(msg.GetThumbnailWidth())
+		if output.ImageHeight == 0 || output.ImageWidth == 0 {
+			src, _, err := image.Decode(bytes.NewReader(thumbnailData))
+			if err == nil {
+				imageBounds := src.Bounds()
+				output.ImageWidth, output.ImageHeight = imageBounds.Max.X, imageBounds.Max.Y
+			}
+		}
+		output.ImageSize = len(thumbnailData)
+		output.ImageType = http.DetectContentType(thumbnailData)
+		uploadData, uploadMime := thumbnailData, output.ImageType
+		if portal.Encrypted {
+			crypto := attachment.NewEncryptedFile()
+			uploadData = crypto.Encrypt(uploadData)
+			uploadMime = "application/octet-stream"
+			output.ImageEncryption = &event.EncryptedFileInfo{EncryptedFile: *crypto}
+		}
+		resp, err := intent.UploadBytes(uploadData, uploadMime)
 		if err != nil {
 			portal.log.Warnfln("Failed to reupload thumbnail for link preview: %v", err)
 		} else {
-			output.Image = mxc.ContentURI.CUString()
-			output.ImageHeight = int(msg.GetThumbnailHeight())
-			output.ImageWidth = int(msg.GetThumbnailWidth())
-			if output.ImageHeight == 0 || output.ImageWidth == 0 {
-				src, _, err := image.Decode(bytes.NewReader(thumbnailData))
-				if err == nil {
-					imageBounds := src.Bounds()
-					output.ImageWidth, output.ImageHeight = imageBounds.Max.X, imageBounds.Max.Y
-				}
+			if output.ImageEncryption != nil {
+				output.ImageEncryption.URL = resp.ContentURI.CUString()
+			} else {
+				output.ImageURL = resp.ContentURI.CUString()
 			}
 		}
 	}
@@ -102,16 +120,9 @@ func (portal *Portal) convertURLPreviewToWhatsApp(sender *User, evt *event.Event
 		return
 	}
 	var preview BeeperLinkPreview
-	if err := json.Unmarshal([]byte(rawPreview.Raw), &preview); err != nil {
+	if err := json.Unmarshal([]byte(rawPreview.Raw), &preview); err != nil || len(preview.MatchedURL) == 0 {
 		return
 	}
-	if len(preview.MatchedURL) == 0 {
-		if len(preview.MatchedURLFallback) == 0 {
-			return
-		} else {
-			preview.MatchedURL = preview.MatchedURLFallback
-		}
-	}
 
 	dest.MatchedText = &preview.MatchedURL
 	if len(preview.CanonicalURL) > 0 {
@@ -123,13 +134,27 @@ func (portal *Portal) convertURLPreviewToWhatsApp(sender *User, evt *event.Event
 	if len(preview.Title) > 0 {
 		dest.Title = &preview.Title
 	}
-	imageMXC := preview.Image.ParseOrIgnore()
+	if strings.HasPrefix(preview.Type, "video.") {
+		dest.PreviewType = waProto.ExtendedTextMessage_VIDEO.Enum()
+	}
+	imageMXC := preview.ImageURL.ParseOrIgnore()
+	if preview.ImageEncryption != nil {
+		imageMXC = preview.ImageEncryption.URL.ParseOrIgnore()
+	}
 	if !imageMXC.IsEmpty() {
 		data, err := portal.MainIntent().DownloadBytes(imageMXC)
 		if err != nil {
-			portal.log.Errorfln("Failed to download URL preview image %s: %v", preview.Image, err)
+			portal.log.Errorfln("Failed to download URL preview image %s in %s: %v", preview.ImageURL, evt.ID, err)
 			return
 		}
+		if preview.ImageEncryption != nil {
+			data, err = preview.ImageEncryption.Decrypt(data)
+			if err != nil {
+				portal.log.Errorfln("Failed to decrypt URL preview image in %s: %v", evt.ID, err)
+				return
+			}
+		}
+		dest.MediaKeyTimestamp = proto.Int64(time.Now().Unix())
 		uploadResp, err := sender.Client.Upload(context.Background(), data, whatsmeow.MediaLinkThumbnail)
 		if err != nil {
 			portal.log.Errorfln("Failed to upload URL preview thumbnail in %s: %v", evt.ID, err)