diff --git a/coolq/bot.go b/coolq/bot.go index 9701a04..320b80d 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -5,6 +5,7 @@ import ( "encoding/gob" "encoding/hex" "fmt" + "github.com/gabriel-vasile/mimetype" "hash/crc32" "io" "os" @@ -76,6 +77,17 @@ func (e *Event) JSONString() string { // ForceFragmented 是否启用强制分片 var ForceFragmented = false +// SkipMimeScan 是否跳过Mime扫描 +var SkipMimeScan bool + +var lawfulImageTypes = []string{"image/png", "image/jpeg", "image/gif", "image/bmp"} + +var lawfulAudioTypes = []string{ + "audio/mpeg", "audio/flac", "audio/midi", "audio/ogg", + "audio/ape", "audio/amr", "audio/wav", "audio/aiff", + "audio/mp4", "audio/aac", "audio/x-m4a", +} + // NewQQBot 初始化一个QQBot实例 func NewQQBot(cli *client.QQClient, conf *config.Config) *CQBot { bot := &CQBot{ @@ -177,12 +189,18 @@ func (bot *CQBot) GetMessage(mid int32) MSG { // UploadLocalImageAsGroup 上传本地图片至群聊 func (bot *CQBot) UploadLocalImageAsGroup(groupCode int64, img *LocalImageElement) (i *message.GroupImageElement, err error) { - if img.Stream != nil { - i, err = bot.Client.UploadGroupImage(groupCode, img.Stream) - } else { - i, err = bot.Client.UploadGroupImageByFile(groupCode, img.File) + if img.File != "" { + f, err := os.Open(img.File) + if err != nil { + return nil, errors.Wrap(err, "open image error") + } + defer func() { _ = f.Close() }() + img.Stream = f } - + if lawful, mime := IsLawfulImage(img.Stream); !lawful { + return nil, errors.New("image type error: " + mime) + } + i, err = bot.Client.UploadGroupImage(groupCode, img.Stream) if i != nil { i.Flash = img.Flash i.EffectID = img.EffectID @@ -570,3 +588,21 @@ func (bot *CQBot) uploadMedia(raw message.IMessageElement, target int64, group b } return nil, errors.New("unsupported message element type") } + +// IsLawfulImage 判断给定流是否为合法图片 +// 返回 是否合法, 实际Mime +// 判断后会自动将 Stream Seek 至 0 +func IsLawfulImage(r io.ReadSeeker) (bool, string) { + if SkipMimeScan { + return true, "" + } + _, _ = r.Seek(0, io.SeekStart) + defer func() { _, _ = r.Seek(0, io.SeekStart) }() + t, err := mimetype.DetectReader(r) + if err != nil { + log.Debugf("扫描 Mime 时出现问题: %v", err) + return false, "" + } + mime := t.String() + return mimetype.EqualsAny(mime, lawfulImageTypes...), mime +} diff --git a/coolq/cqcode.go b/coolq/cqcode.go index c65cd07..921c984 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -8,6 +8,7 @@ import ( xml2 "encoding/xml" "errors" "fmt" + "github.com/gabriel-vasile/mimetype" "io" "math/rand" "net/url" @@ -728,6 +729,12 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte if err != nil { return nil, err } + if !SkipMimeScan && !global.IsAMRorSILK(data) { + mt := mimetype.Detect(data).String() + if !mimetype.EqualsAny(mt, lawfulAudioTypes...) { + return nil, errors.New("audio type error: " + mt) + } + } if !global.IsAMRorSILK(data) { data, err = global.EncoderSilk(data) if err != nil { diff --git a/global/config/config.go b/global/config/config.go index f51c542..e0f58b2 100644 --- a/global/config/config.go +++ b/global/config/config.go @@ -56,6 +56,7 @@ type Config struct { ReportSelfMessage bool `yaml:"report-self-message"` RemoveReplyAt bool `yaml:"remove-reply-at"` ExtraReplyData bool `yaml:"extra-reply-data"` + SkipMimeScan bool `yaml:"skip-mime-scan"` } `yaml:"message"` Output struct { diff --git a/global/config/default_config.yml b/global/config/default_config.yml index 802c271..ca7affd 100644 --- a/global/config/default_config.yml +++ b/global/config/default_config.yml @@ -39,6 +39,8 @@ message: remove-reply-at: false # 为Reply附加更多信息 extra-reply-data: false + # 跳过 Mime 扫描, 忽略错误数据 + skip-mime-scan: false output: # 日志等级 trace,debug,info,warn,error diff --git a/go.mod b/go.mod index 8df24db..a1f6493 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Microsoft/go-winio v0.5.0 github.com/Mrs4s/MiraiGo v0.0.0-20210726103104-1d68826cef0e github.com/dustin/go-humanize v1.0.0 + github.com/gabriel-vasile/mimetype v1.3.1 // indirect github.com/gorilla/websocket v1.4.2 github.com/guonaihong/gout v0.2.1 github.com/jonboulle/clockwork v0.2.2 // indirect diff --git a/go.sum b/go.sum index 88c6d68..ce9475e 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= +github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.0 h1:Lb3veSYoGaNck69fV2+Vf2juLSsHpMTf3Vk5+X+EDJg= @@ -150,6 +152,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -164,6 +168,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/main.go b/main.go index c2a5d38..52f3da2 100644 --- a/main.go +++ b/main.go @@ -365,6 +365,7 @@ func main() { coolq.ForceFragmented = conf.Message.ForceFragment coolq.RemoveReplyAt = conf.Message.RemoveReplyAt coolq.ExtraReplyData = conf.Message.ExtraReplyData + coolq.SkipMimeScan = conf.Message.SkipMimeScan for _, m := range conf.Servers { if h, ok := m["http"]; ok { hc := new(config.HTTPServer)