commit eb32d68a28087483a9530a82d508e68d31649d7d
Author: selfhoster1312 <selfhoster1312@kl.netlib.re>
Date: Sat Dec 06 20:50:08 2025 +0000
diff --git a/bridge/bridge.go b/bridge/bridge.go
index 3511693..0451ecb 100644
--- a/bridge/bridge.go
+++ b/bridge/bridge.go
@@ -16 +110 @@
package bridge
import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
"log"
"net/http"
"net/url"
@@ -176 +218 @@ type Bridger interface {
Connect() error
JoinChannel(channel config.ChannelInfo) error
Disconnect() error
+ NewHttpRequest(method, uri string, body io.Reader) (*http.Request, error)
+ NewHttpClient(proxy string) (*http.Client, error)
}
type Bridge struct {
@@ -1703 +176138 @@ func (b *Bridge) NewHttpClient(http_proxy string) (*http.Client, error) {
Timeout: time.Second * 5,
}, nil
}
+
+var errHttpGetNotOk = errors.New("HTTP server responded non-OK code")
+
+func HttpGetNotOkError(uri string, code int) error {
+ return fmt.Errorf("%w: %s returned code %d", errHttpGetNotOk, uri, code)
+}
+
+func (b *Bridge) HttpGetBytes(uri string) (*[]byte, error) {
+ req, err := b.Bridger.NewHttpRequest("GET", uri, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ b.Log.Debugf("Getting HTTP bytes with request: %#v", req)
+
+ resp, err := b.HttpClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, HttpGetNotOkError(uri, resp.StatusCode)
+ }
+
+ var buf bytes.Buffer
+
+ _, err = io.Copy(&buf, resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ err = resp.Body.Close()
+ if err != nil {
+ return nil, err
+ }
+
+ data := buf.Bytes()
+
+ return &data, nil
+}
+
+func (b *Bridge) AddAttachmentFromURL(msg *config.Message, filename string, id string, comment string, uri string) error {
+ return b.addAttachment(msg, filename, id, comment, uri, nil, false)
+}
+
+func (b *Bridge) AddAttachmentFromBytes(msg *config.Message, filename string, id string, comment string, data *[]byte) error {
+ return b.addAttachment(msg, filename, id, comment, "", data, false)
+}
+
+func (b *Bridge) AddAvatarFromURL(msg *config.Message, filename string, id string, comment string, uri string) error {
+ return b.addAttachment(msg, filename, id, comment, uri, nil, true)
+}
+
+func (b *Bridge) AddAvatarFromBytes(msg *config.Message, filename string, id string, comment string, data *[]byte) error {
+ return b.addAttachment(msg, filename, id, comment, "", data, true)
+}
+
+// NewHttpRequest produces a new http.Request instance with bridge-specific settings.
+//
+// This is used by bridges where HTTP downloads require a cookie/token, by overriding
+// this method in the bridge struct.
+func (b *Bridge) NewHttpRequest(method, uri string, body io.Reader) (*http.Request, error) {
+ return http.NewRequest(method, uri, body)
+}
+
+// Internal method including common parts to attachment/avatar handling methods.
+//
+// This method will process received bytes. If bytes are not set, they will be downloaded from the given URL.
+// If neither data bytes nor uri is provided, this will be a hard error because there's a logic error somewhere.
+func (b *Bridge) addAttachment(msg *config.Message, filename string, id string, comment string, uri string, data *[]byte, avatar bool) error {
+ if data != nil {
+ return b.addAttachmentProcess(msg, filename, id, comment, uri, data, avatar)
+ }
+
+ if uri == "" {
+ // This should never happen
+ b.Log.Fatalf("Logic error in bridge %s: attachment should have either URL or data set, neither was provided", b.Protocol)
+ }
+
+ data, err := b.HttpGetBytes(uri)
+ if err != nil {
+ return err
+ }
+
+ return b.addAttachmentProcess(msg, filename, id, comment, uri, data, avatar)
+}
+
+type errFileTooLarge struct {
+ FileName string
+ Size int
+ MaxSize int
+}
+
+func (e *errFileTooLarge) Error() string {
+ return fmt.Sprintf("File %#v to large to download (%#v). MediaDownloadSize is %#v", e.FileName, e.Size, e.MaxSize)
+}
+
+type errFileBlacklisted struct {
+ FileName string
+}
+
+func (e *errFileBlacklisted) Error() string {
+ return fmt.Sprintf("File %#v matches the backlist, not downloading it", e.FileName)
+}
+
+func (b *Bridge) addAttachmentProcess(msg *config.Message, filename string, id string, comment string, uri string, data *[]byte, avatar bool) error {
+ size := len(*data)
+ if size > b.General.MediaDownloadSize {
+ return &errFileTooLarge{
+ FileName: filename,
+ Size: size,
+ MaxSize: b.General.MediaDownloadSize,
+ }
+ }
+
+ // Apply `MediaDownloadBlackList` regexes
+ if b.Config.IsFilenameBlacklisted(filename) {
+ return &errFileBlacklisted{
+ FileName: filename,
+ }
+ }
+
+ b.Log.Debugf("Download OK %#v %#v", filename, size)
+ msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{
+ Name: filename,
+ Data: data,
+ URL: uri,
+ Comment: comment,
+ Avatar: avatar,
+ // TODO: if id is not set, maybe use hash of bytes?
+ NativeID: id,
+ })
+
+ return nil
+}