diff --git a/protocol/messenger.go b/protocol/messenger.go index eeb838c2fe..dbc01b0592 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -14,6 +14,7 @@ import ( "math/rand" "os" "reflect" + "strings" "sync" "time" @@ -4164,6 +4165,35 @@ func (m *Messenger) prepareMessage(msg *common.Message, s *server.MediaServer) { if msg.ContentType == protobuf.ChatMessage_IMAGE { msg.ImageLocalURL = s.MakeImageURL(msg.ID) } + if msg.ContentType == protobuf.ChatMessage_DISCORD_MESSAGE { + + dm := msg.GetDiscordMessage() + exists, err := m.persistence.HasDiscordMessageAuthorImagePayload(dm.Author.Id) + if err != nil { + return + } + + if exists { + dm.Author.LocalUrl = s.MakeDiscordAuthorAvatarURL(dm.Author.Id) + } + + for idx, attachment := range dm.Attachments { + if strings.Contains(attachment.ContentType, "image") { + hasPayload, err := m.persistence.HasDiscordMessageAttachmentPayload(attachment.Id) + if err != nil { + m.logger.Error("failed to check if message attachment exist", zap.Error(err)) + continue + } + if hasPayload { + localURL := s.MakeDiscordAttachmentURL(dm.Id, attachment.Id) + dm.Attachments[idx].LocalUrl = localURL + } + } + } + msg.Payload = &protobuf.ChatMessage_DiscordMessage{ + DiscordMessage: dm, + } + } if msg.ContentType == protobuf.ChatMessage_AUDIO { msg.AudioLocalURL = s.MakeAudioURL(msg.ID) } diff --git a/server/handlers.go b/server/handlers.go index f081c8ac11..68e2b14fad 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -25,11 +25,13 @@ import ( ) const ( - basePath = "/messages" - identiconsPath = basePath + "/identicons" - imagesPath = basePath + "/images" - audioPath = basePath + "/audio" - ipfsPath = "/ipfs" + basePath = "/messages" + identiconsPath = basePath + "/identicons" + imagesPath = basePath + "/images" + audioPath = basePath + "/audio" + ipfsPath = "/ipfs" + discordAuthorsPath = "/discord/authors" + discordAttachmentsPath = basePath + "/discord/attachments" // Handler routes for pairing pairingBase = "/pairing" @@ -236,6 +238,79 @@ func handleIdenticon(logger *zap.Logger) http.HandlerFunc { } } +func handleDiscordAuthorAvatar(db *sql.DB, logger *zap.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + authorIDs, ok := r.URL.Query()["authorId"] + if !ok || len(authorIDs) == 0 { + logger.Error("no authorIDs") + return + } + authorID := authorIDs[0] + + var image []byte + err := db.QueryRow(`SELECT avatar_image_payload FROM discord_message_authors WHERE id = ?`, authorID).Scan(&image) + if err != nil { + logger.Error("failed to find image", zap.Error(err)) + return + } + if len(image) == 0 { + logger.Error("empty image") + return + } + mime, err := images.ImageMime(image) + if err != nil { + logger.Error("failed to get mime", zap.Error(err)) + } + + w.Header().Set("Content-Type", mime) + w.Header().Set("Cache-Control", "no-store") + + _, err = w.Write(image) + if err != nil { + logger.Error("failed to write image", zap.Error(err)) + } + } +} + +func handleDiscordAttachment(db *sql.DB, logger *zap.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + messageIDs, ok := r.URL.Query()["messageId"] + if !ok || len(messageIDs) == 0 { + logger.Error("no messageID") + return + } + attachmentIDs, ok := r.URL.Query()["attachmentId"] + if !ok || len(attachmentIDs) == 0 { + logger.Error("no attachmentID") + return + } + messageID := messageIDs[0] + attachmentID := attachmentIDs[0] + var image []byte + err := db.QueryRow(`SELECT payload FROM discord_message_attachments WHERE discord_message_id = ? AND id = ?`, messageID, attachmentID).Scan(&image) + if err != nil { + logger.Error("failed to find image", zap.Error(err)) + return + } + if len(image) == 0 { + logger.Error("empty image") + return + } + mime, err := images.ImageMime(image) + if err != nil { + logger.Error("failed to get mime", zap.Error(err)) + } + + w.Header().Set("Content-Type", mime) + w.Header().Set("Cache-Control", "no-store") + + _, err = w.Write(image) + if err != nil { + logger.Error("failed to write image", zap.Error(err)) + } + } +} + func handleImage(db *sql.DB, logger *zap.Logger) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { messageIDs, ok := r.URL.Query()["messageId"] diff --git a/server/server_media.go b/server/server_media.go index 6dcea27a9d..e625a5d7da 100644 --- a/server/server_media.go +++ b/server/server_media.go @@ -33,12 +33,14 @@ func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader, multiaccountsDB *mu multiaccountsDB: multiaccountsDB, } s.SetHandlers(HandlerPatternMap{ - imagesPath: handleImage(s.db, s.logger), - audioPath: handleAudio(s.db, s.logger), - identiconsPath: handleIdenticon(s.logger), - ipfsPath: handleIPFS(s.downloader, s.logger), - accountImagesPath: handleAccountImages(s.multiaccountsDB, s.logger), - contactImagesPath: handleContactImages(s.db, s.logger), + imagesPath: handleImage(s.db, s.logger), + audioPath: handleAudio(s.db, s.logger), + identiconsPath: handleIdenticon(s.logger), + ipfsPath: handleIPFS(s.downloader, s.logger), + accountImagesPath: handleAccountImages(s.multiaccountsDB, s.logger), + contactImagesPath: handleContactImages(s.db, s.logger), + discordAuthorsPath: handleDiscordAuthorAvatar(s.db, s.logger), + discordAttachmentsPath: handleDiscordAttachment(s.db, s.logger), }) return s, nil @@ -66,6 +68,22 @@ func (s *MediaServer) MakeImageURL(id string) string { return u.String() } +func (s *MediaServer) MakeDiscordAuthorAvatarURL(authorID string) string { + u := s.MakeBaseURL() + u.Path = discordAuthorsPath + u.RawQuery = url.Values{"authorId": {authorID}}.Encode() + + return u.String() +} + +func (s *MediaServer) MakeDiscordAttachmentURL(messageID string, id string) string { + u := s.MakeBaseURL() + u.Path = discordAttachmentsPath + u.RawQuery = url.Values{"messageId": {messageID}, "attachmentId": {id}}.Encode() + + return u.String() +} + func (s *MediaServer) MakeAudioURL(id string) string { u := s.MakeBaseURL() u.Path = audioPath