diff --git a/bridge/telegram/handlers.go b/bridge/telegram/handlers.go index 8e7dc7ba..f17404d2 100644 --- a/bridge/telegram/handlers.go +++ b/bridge/telegram/handlers.go @@ -346,7 +346,7 @@ func (b *Btelegram) handleDownloadAvatar(userid int64, channel string) { b.Log.Errorf("download %s failed %#v", url, err) return } - helper.HandleDownloadData(b.Log, &rmsg, name, rmsg.Text, "", data, b.General) + helper.HandleDownloadData(b.Log, &rmsg, name, rmsg.Text, url, data, b.General) b.Remote <- rmsg } } @@ -440,7 +440,7 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa name = strings.Replace(name, ".oga", ".ogg", 1) } - helper.HandleDownloadData(b.Log, rmsg, name, message.Caption, "", data, b.General) + helper.HandleDownloadData(b.Log, rmsg, name, message.Caption, url, data, b.General) return nil } @@ -513,9 +513,22 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, threadid var media []interface{} for _, f := range msg.Extra["file"] { fi := f.(config.FileInfo) + + // If the file is only a URL, download it first + data := fi.Data + + if data == nil { + data2, err := helper.DownloadFile(fi.URL) + if err != nil { + b.Log.Errorf("Failed to download file %s: %s", fi.URL, err) + continue + } + data = data2 + } + file := tgbotapi.FileBytes{ Name: fi.Name, - Bytes: *fi.Data, + Bytes: *data, } if b.GetString("MessageFormat") == HTMLFormat { @@ -525,21 +538,15 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, threadid switch filepath.Ext(fi.Name) { case ".jpg", ".jpe", ".png": pc := tgbotapi.NewInputMediaPhoto(file) - if fi.Comment != "" { - pc.Caption, pc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) - } + pc.Caption, pc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) media = append(media, pc) case ".mp4", ".m4v": vc := tgbotapi.NewInputMediaVideo(file) - if fi.Comment != "" { - vc.Caption, vc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) - } + vc.Caption, vc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) media = append(media, vc) case ".mp3", ".oga": ac := tgbotapi.NewInputMediaAudio(file) - if fi.Comment != "" { - ac.Caption, ac.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) - } + ac.Caption, ac.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) media = append(media, ac) case ".ogg": voc := tgbotapi.NewVoice(chatid, file) @@ -552,9 +559,7 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, threadid return strconv.Itoa(res.MessageID), nil default: dc := tgbotapi.NewInputMediaDocument(file) - if fi.Comment != "" { - dc.Caption, dc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) - } + dc.Caption, dc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) media = append(media, dc) } } diff --git a/bridge/xmpp/helpers.go b/bridge/xmpp/helpers.go index eb6a5366..07613dd8 100644 --- a/bridge/xmpp/helpers.go +++ b/bridge/xmpp/helpers.go @@ -7,6 +7,7 @@ import ( ) var pathRegex = regexp.MustCompile("[^a-zA-Z0-9]+") +var urlRegex = regexp.MustCompile("(.*?)") // GetAvatar constructs a URL for a given user-avatar if it is available in the cache. func getAvatar(av map[string]string, userid string, general *config.Protocol) string { @@ -28,3 +29,11 @@ func (b *Bxmpp) cacheAvatar(msg *config.Message) string { } return "" } + +func (b *Bxmpp) findOOBURL(innerXML string) string { + match := urlRegex.FindStringSubmatch(innerXML) + if match == nil { + return "" + } + return match[1] +} diff --git a/bridge/xmpp/xmpp.go b/bridge/xmpp/xmpp.go index cf58a2fc..c3d1f214 100644 --- a/bridge/xmpp/xmpp.go +++ b/bridge/xmpp/xmpp.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "net/url" + "path" "strings" "sync" "time" @@ -330,6 +331,7 @@ func (b *Bxmpp) handleXMPP() error { UserID: v.Remote, ID: msgID, Event: event, + Extra: make(map[string][]interface{}), } // Check if we have an action event. @@ -339,6 +341,32 @@ func (b *Bxmpp) handleXMPP() error { rmsg.Event = config.EventUserAction } + // Check if maybe OOB file upload + for _, elem := range v.OtherElem { + if elem.XMLName.Space == "jabber:x:oob" { + // Extract the plaintext URL + url := b.findOOBURL(elem.InnerXML) + // Now the filename in the URL + r,_ := http.NewRequest("GET", url, nil) + fileName := path.Base(r.URL.Path) + + if url != "" { + if rmsg.Extra["file"] == nil { + rmsg.Extra["file"] = make([]interface{}, 0) + } + rmsg.Extra["file"] = append(rmsg.Extra["file"], config.FileInfo{ + Name: fileName, + Data: nil, + URL: url, + Comment: "", + Avatar: false, + NativeID: url, + }) + + } + } + } + b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) b.Log.Debugf("<= Message is %#v", rmsg) b.Remote <- rmsg @@ -361,38 +389,46 @@ func (b *Bxmpp) replaceAction(text string) (string, bool) { } // handleUploadFile handles native upload of files +// +// IMPORTANT NOTES: +// +// Some clients only display a preview when the body is exactly the URL, not only contains it. +// https://docs.modernxmpp.org/client/protocol/#communicating-the-url +// +// This is the case with Gajim/Conversations for example. +// +// This means we cannot have an actual description of the uploaded file, nor can we add information about +// who posted it... at least in the same message. func (b *Bxmpp) handleUploadFile(msg *config.Message) error { - var urlDesc string - for _, file := range msg.Extra["file"] { fileInfo := file.(config.FileInfo) - if fileInfo.Comment != "" { - msg.Text += fileInfo.Comment + ": " - } - if fileInfo.URL != "" { - msg.Text = fileInfo.URL - if fileInfo.Comment != "" { - msg.Text = fileInfo.Comment + ": " + fileInfo.URL - urlDesc = fileInfo.Comment - } - } - if _, err := b.xc.Send(xmpp.Chat{ - Type: "groupchat", - Remote: msg.Channel + "@" + b.GetString("Muc"), - Text: msg.Username + msg.Text, - }); err != nil { - return err - } if fileInfo.URL != "" { - if _, err := b.xc.SendOOB(xmpp.Chat{ - Type: "groupchat", - Remote: msg.Channel + "@" + b.GetString("Muc"), - Ooburl: fileInfo.URL, - Oobdesc: urlDesc, + // We have a URL, no need to reupload the file + + // Send separate message with the username and optional file comment + if _, err := b.xc.Send(xmpp.Chat{ + Type: "groupchat", + Remote: msg.Channel + "@" + b.GetString("Muc"), + Text: msg.Username + fileInfo.Comment, + }); err != nil { + b.Log.WithError(err).Warn("Failed to announce file sharer, not sharing file.") + } + + if _, err := b.xc.Send(xmpp.Chat{ + Type: "groupchat", + Remote: msg.Channel + "@" + b.GetString("Muc"), + // See NOTES above + Text: fileInfo.URL, + Ooburl: fileInfo.URL, + Oobdesc: msg.Username, }); err != nil { b.Log.WithError(err).Warn("Failed to send share URL.") + continue } + } else { + // We have Bytes, need to reupload the file using HTTP upload from our XMPP server + b.Log.Warn("HTTP file upload on XMPP is not supported yet") } } return nil