Replaced h2non/filetype with net/http DetectContentType()
This commit is contained in:
parent
c06b536123
commit
a3f8a85d28
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version string = "0.66.2"
|
Version string = "0.67.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -6,8 +6,6 @@ package formats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterAudioFormats() *SupportedFormat {
|
func RegisterAudioFormats() *SupportedFormat {
|
||||||
|
@ -25,10 +23,13 @@ func RegisterAudioFormats() *SupportedFormat {
|
||||||
Extensions: []string{
|
Extensions: []string{
|
||||||
`.mp3`,
|
`.mp3`,
|
||||||
`.ogg`,
|
`.ogg`,
|
||||||
|
`.oga`,
|
||||||
`.wav`,
|
`.wav`,
|
||||||
},
|
},
|
||||||
validator: func(head []byte) bool {
|
MimeTypes: []string{
|
||||||
return filetype.IsAudio(head)
|
`audio/mpeg`,
|
||||||
|
`audio/ogg`,
|
||||||
|
`audio/wav`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
_ "golang.org/x/image/bmp"
|
_ "golang.org/x/image/bmp"
|
||||||
_ "golang.org/x/image/webp"
|
_ "golang.org/x/image/webp"
|
||||||
)
|
)
|
||||||
|
@ -53,13 +52,17 @@ func RegisterImageFormats() *SupportedFormat {
|
||||||
Extensions: []string{
|
Extensions: []string{
|
||||||
`.bmp`,
|
`.bmp`,
|
||||||
`.gif`,
|
`.gif`,
|
||||||
`.jpeg`,
|
|
||||||
`.jpg`,
|
`.jpg`,
|
||||||
|
`.jpeg`,
|
||||||
`.png`,
|
`.png`,
|
||||||
`.webp`,
|
`.webp`,
|
||||||
},
|
},
|
||||||
validator: func(head []byte) bool {
|
MimeTypes: []string{
|
||||||
return filetype.IsImage(head)
|
`image/bmp`,
|
||||||
|
`image/gif`,
|
||||||
|
`image/jpeg`,
|
||||||
|
`image/png`,
|
||||||
|
`image/webp`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +79,7 @@ func ImageDimensions(path string) (*Dimensions, error) {
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
myImage, _, err := image.DecodeConfig(file)
|
decodedConfig, _, err := image.DecodeConfig(file)
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, image.ErrFormat):
|
case errors.Is(err, image.ErrFormat):
|
||||||
fmt.Printf("File %s has invalid image format\n", path)
|
fmt.Printf("File %s has invalid image format\n", path)
|
||||||
|
@ -86,5 +89,5 @@ func ImageDimensions(path string) (*Dimensions, error) {
|
||||||
return &Dimensions{}, err
|
return &Dimensions{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Dimensions{Width: myImage.Width, Height: myImage.Height}, nil
|
return &Dimensions{Width: decodedConfig.Width, Height: decodedConfig.Height}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,17 @@ package formats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FormatFunction func(queryParams, fileUri, filePath, fileName, mime string) string
|
type FormatFunction func(queryParams, fileUri, filePath, fileName, mime string) string
|
||||||
type ValidatorFunction func([]byte) bool
|
|
||||||
|
|
||||||
type SupportedFormat struct {
|
type SupportedFormat struct {
|
||||||
Title FormatFunction
|
Title FormatFunction
|
||||||
Body FormatFunction
|
Body FormatFunction
|
||||||
Extensions []string
|
Extensions []string
|
||||||
validator ValidatorFunction
|
MimeTypes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SupportedFormats struct {
|
type SupportedFormats struct {
|
||||||
|
@ -32,19 +28,29 @@ func (s *SupportedFormats) Add(t *SupportedFormat) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SupportedFormats) Extensions() []string {
|
func (s *SupportedFormats) Extensions() []string {
|
||||||
var r []string
|
var extensions []string
|
||||||
|
|
||||||
for _, t := range s.types {
|
for _, t := range s.types {
|
||||||
r = append(r, t.Extensions...)
|
extensions = append(extensions, t.Extensions...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SupportedFormats) Type(extension string) *SupportedFormat {
|
func (s *SupportedFormats) MimeTypes() []string {
|
||||||
|
var mimeTypes []string
|
||||||
|
|
||||||
|
for _, t := range s.types {
|
||||||
|
mimeTypes = append(mimeTypes, t.MimeTypes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mimeTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SupportedFormats) Type(mimeType string) *SupportedFormat {
|
||||||
for i := range s.types {
|
for i := range s.types {
|
||||||
for _, e := range s.types[i].Extensions {
|
for _, m := range s.types[i].MimeTypes {
|
||||||
if extension == e {
|
if mimeType == m {
|
||||||
return s.types[i]
|
return s.types[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,18 +59,6 @@ func (s *SupportedFormats) Type(extension string) *SupportedFormat {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SupportedFormats) IsSupported(head []byte) bool {
|
|
||||||
r := false
|
|
||||||
|
|
||||||
for i := range s.types {
|
|
||||||
if s.types[i].validator(head) {
|
|
||||||
r = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func FileType(path string, types *SupportedFormats) (bool, *SupportedFormat, string, error) {
|
func FileType(path string, types *SupportedFormats) (bool, *SupportedFormat, string, error) {
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
switch {
|
switch {
|
||||||
|
@ -75,22 +69,18 @@ func FileType(path string, types *SupportedFormats) (bool, *SupportedFormat, str
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
head := make([]byte, 261)
|
head := make([]byte, 512)
|
||||||
file.Read(head)
|
file.Read(head)
|
||||||
|
|
||||||
if types.IsSupported(head) {
|
mimeType := http.DetectContentType(head)
|
||||||
extension := filepath.Ext(path)
|
|
||||||
|
|
||||||
for _, v := range types.Extensions() {
|
for _, v := range types.MimeTypes() {
|
||||||
if extension == v {
|
if mimeType == v {
|
||||||
fileType := types.Type(extension)
|
fileType := types.Type(mimeType)
|
||||||
|
|
||||||
mimeType := (filetype.GetType(strings.TrimPrefix(extension, "."))).MIME.Value
|
|
||||||
|
|
||||||
return true, fileType, mimeType, nil
|
return true, fileType, mimeType, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil, "", nil
|
return false, nil, "", nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@ package formats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterVideoFormats() *SupportedFormat {
|
func RegisterVideoFormats() *SupportedFormat {
|
||||||
|
@ -27,8 +25,10 @@ func RegisterVideoFormats() *SupportedFormat {
|
||||||
`.ogv`,
|
`.ogv`,
|
||||||
`.webm`,
|
`.webm`,
|
||||||
},
|
},
|
||||||
validator: func(head []byte) bool {
|
MimeTypes: []string{
|
||||||
return filetype.IsVideo(head)
|
`video/mp4`,
|
||||||
|
`video/ogg`,
|
||||||
|
`video/webm`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -3,7 +3,6 @@ module seedno.de/seednode/roulette
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/h2non/filetype v1.1.3
|
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
github.com/klauspost/compress v1.16.7
|
github.com/klauspost/compress v1.16.7
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1,6 +1,4 @@
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
|
||||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
The MIT License
|
|
||||||
|
|
||||||
Copyright (c) Tomas Aparicio
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person
|
|
||||||
obtaining a copy of this software and associated documentation
|
|
||||||
files (the "Software"), to deal in the Software without
|
|
||||||
restriction, including without limitation the rights to use,
|
|
||||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following
|
|
||||||
conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,12 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = tabs
|
|
||||||
indent_size = 2
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
|
@ -1,2 +0,0 @@
|
||||||
bin
|
|
||||||
.DS_Store
|
|
|
@ -1,16 +0,0 @@
|
||||||
language: go
|
|
||||||
arch:
|
|
||||||
- AMD64
|
|
||||||
- ppc64le
|
|
||||||
go:
|
|
||||||
- "1.13"
|
|
||||||
- "1.14"
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get -u -v golang.org/x/lint/golint
|
|
||||||
|
|
||||||
script:
|
|
||||||
- diff -u <(echo -n) <(gofmt -s -d ./)
|
|
||||||
- diff -u <(echo -n) <(go vet ./...)
|
|
||||||
- diff -u <(echo -n) <(golint)
|
|
||||||
- go test -v -race ./...
|
|
|
@ -1,163 +0,0 @@
|
||||||
|
|
||||||
v1.0.3 / 2021-11-21
|
|
||||||
===================
|
|
||||||
|
|
||||||
* fix(#108): add application file matchers
|
|
||||||
* Merge pull request #106 from hannesbraun/aiff-support
|
|
||||||
* Add AIFF support
|
|
||||||
* fix(archive): format issue indentation
|
|
||||||
* feat(version): bump patch
|
|
||||||
* Merge pull request #100 from da2018/master
|
|
||||||
* Enhance Zstd support
|
|
||||||
* Merge pull request #98 from cfergeau/zstd
|
|
||||||
* Add zstd support
|
|
||||||
* Merge pull request #99 from cfergeau/byteprefixmatcher
|
|
||||||
* Introduce bytePrefixMatcher helper
|
|
||||||
|
|
||||||
v1.1.0 / 2020-06-06
|
|
||||||
===================
|
|
||||||
|
|
||||||
* feat: version bump v1.10
|
|
||||||
* feat(ci): add go 1.14
|
|
||||||
* Merge pull request #82 from andrewstucki/sqlite-update
|
|
||||||
* Merge pull request #84 from evanoberholster/master
|
|
||||||
* Better differentiation: between image/x-canon-cr2 and image/tiff
|
|
||||||
* Merge pull request #1 from h2non/master
|
|
||||||
* Update ico filetype per https://www.iana.org/assignments/media-types/image/vnd.microsoft.icon
|
|
||||||
* Update rar filetype per https://www.iana.org/assignments/media-types/application/vnd.rar
|
|
||||||
* Update exe filetype per https://www.iana.org/assignments/media-types/application/vnd.microsoft.portable-executable
|
|
||||||
* Update deb filetype per https://www.iana.org/assignments/media-types/application/vnd.debian.binary-package
|
|
||||||
* Update sqlite filetype per https://www.iana.org/assignments/media-types/application/vnd.sqlite3
|
|
||||||
* Merge pull request #72 from turn88/master
|
|
||||||
* Update document.go
|
|
||||||
* Update document.go
|
|
||||||
* Update document.go
|
|
||||||
* add matchers for office 2003
|
|
||||||
|
|
||||||
v1.0.10 / 2019-08-06
|
|
||||||
====================
|
|
||||||
|
|
||||||
* Merge pull request #76 from lex-r/fix-matroska-detection
|
|
||||||
* fix: mkv and webm types detection
|
|
||||||
|
|
||||||
v1.0.9 / 2019-07-25
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Merge pull request #75 from Trane9991/master
|
|
||||||
* add video/3gpp support
|
|
||||||
* fix: use proper iso file mime type
|
|
||||||
* feat: add iso image format
|
|
||||||
* Merge pull request #65 from Fentonz/master
|
|
||||||
* Merge pull request #70 from fanpei91/master
|
|
||||||
* add image/vnd.dwg to README
|
|
||||||
* add image/vnd.dwg support
|
|
||||||
* Added support for .iso files
|
|
||||||
|
|
||||||
v1.0.8 / 2019-02-10
|
|
||||||
===================
|
|
||||||
|
|
||||||
* refactor(images): heic -> heif
|
|
||||||
* feat(docs): add heif format
|
|
||||||
* Merge pull request #60 from rikonor/master
|
|
||||||
* add heif/heic support
|
|
||||||
* fix(docs): dicom -> dcm
|
|
||||||
* feat: add dicom type
|
|
||||||
* Merge pull request #58 from Fentonz/master
|
|
||||||
* Merge pull request #59 from kmanley/master
|
|
||||||
* fix example; related to h2non/filetype#43
|
|
||||||
* Added DICOM type to archive
|
|
||||||
|
|
||||||
|
|
||||||
v1.0.7 / 2019-02-09
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Merge pull request #56 from akupila/wasm
|
|
||||||
* add wasm to readme
|
|
||||||
* detect wasm file type
|
|
||||||
|
|
||||||
v1.0.6 / 2019-01-22
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Merge pull request #55 from ivanlemeshev/master
|
|
||||||
* Added ftypmp4v to MP4 matcher
|
|
||||||
* Merge pull request #54 from aofei/master
|
|
||||||
* chore: add support for Go modules
|
|
||||||
* feat: add support for AAC (audio/aac)
|
|
||||||
* Merge pull request #53 from lynxbyorion/check-for-docoments
|
|
||||||
* Added checks for documents.
|
|
||||||
* Merge pull request #51 from eriken/master
|
|
||||||
* fixed bad mime and import paths
|
|
||||||
* Merge pull request #50 from eriken/jpeg2000_support
|
|
||||||
* fix import paths
|
|
||||||
* jpeg2000 support
|
|
||||||
* Merge pull request #47 from Ma124/master
|
|
||||||
* Merge pull request #49 from amoore614/master
|
|
||||||
* more robust check for .mov files
|
|
||||||
* bugfix: reverse order of matcher key list so user registered matchers appear first
|
|
||||||
* bugfix: store ptr to MatcherKeys in case user registered matchers are used.
|
|
||||||
* update comment
|
|
||||||
* Bump buffer size to 8K to allow for more custom file matching
|
|
||||||
* refactor(readme): update package import path
|
|
||||||
* Merge pull request #48 from kumakichi/support_msooxml
|
|
||||||
* do not use v1
|
|
||||||
* ok, master already changed travis
|
|
||||||
* add fixtures, but MatchReader may not work for some msooxml files, 4096 bytes maybe not enough
|
|
||||||
* support ms ooxml, #40
|
|
||||||
* Fixed misspells
|
|
||||||
* fix(travis): use string notation for matrix items
|
|
||||||
* Merge pull request #42 from bruth/patch-2
|
|
||||||
* refactor(travis): remove Go 1.6, add Go 1.10
|
|
||||||
* Change maximum bytes required for detection
|
|
||||||
* Merge pull request #36 from yiiTT/patch-1
|
|
||||||
* Add MP4 dash and additional ISO formats
|
|
||||||
* Merge pull request #34 from RangelReale/fix-mp4-case
|
|
||||||
* Merge pull request #32 from yiiTT/fix-m4v
|
|
||||||
* Fixed mp4 detection case-sensitivity according to http://www.ftyps.com/
|
|
||||||
* Fix M4v matcher
|
|
||||||
|
|
||||||
v1.0.5 / 2017-12-12
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Merge pull request #30 from RangelReale/fix_mp4
|
|
||||||
* Fix duplicated item in mp4 fix
|
|
||||||
* Fix MP4 matcher, with information from http://www.file-recovery.com/mp4-signature-format.htm
|
|
||||||
* Merge pull request #28 from ikovic/master
|
|
||||||
* Updated file header example.
|
|
||||||
|
|
||||||
v1.0.4 / 2017-11-29
|
|
||||||
===================
|
|
||||||
|
|
||||||
* fix: tests and document types matchers
|
|
||||||
* refactor(docs): remove codesponsor
|
|
||||||
* Merge pull request #26 from bienkma/master
|
|
||||||
* Add support check file type: .doc, .docx, .pptx, .ppt, .xls, .xlsx
|
|
||||||
* feat(docs): add code sponsor banner
|
|
||||||
* feat(travis): add go 1.9
|
|
||||||
* Merge pull request #24 from strazzere/patch-1
|
|
||||||
* Fix typo in unknown
|
|
||||||
|
|
||||||
v1.0.3 / 2017-08-03
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Merge pull request #21 from elemeta/master
|
|
||||||
* Add Elf file as supported matcher archive type
|
|
||||||
|
|
||||||
v1.0.2 / 2017-07-26
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Merge pull request #20 from marshyski/master
|
|
||||||
* Added RedHat RPM as supported matcher archive type
|
|
||||||
* Merge pull request #19 from nlamirault/patch-1
|
|
||||||
* Fix typo in documentation
|
|
||||||
|
|
||||||
v1.0.1 / 2017-02-24
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Merge pull request #18 from Impyy/enable-webm
|
|
||||||
* Enable the webm matcher
|
|
||||||
* feat(docs): add Go version badge
|
|
||||||
|
|
||||||
1.0.0 / 2016-12-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
- Initial stable version (v1.0.0).
|
|
|
@ -1,24 +0,0 @@
|
||||||
The MIT License
|
|
||||||
|
|
||||||
Copyright (c) Tomas Aparicio
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person
|
|
||||||
obtaining a copy of this software and associated documentation
|
|
||||||
files (the "Software"), to deal in the Software without
|
|
||||||
restriction, including without limitation the rights to use,
|
|
||||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following
|
|
||||||
conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,294 +0,0 @@
|
||||||
# filetype [![Build Status](https://travis-ci.org/h2non/filetype.svg)](https://travis-ci.org/h2non/filetype) [![GoDoc](https://godoc.org/github.com/h2non/filetype?status.svg)](https://godoc.org/github.com/h2non/filetype) [![Go Report Card](http://goreportcard.com/badge/h2non/filetype)](http://goreportcard.com/report/h2non/filetype) [![Go Version](https://img.shields.io/badge/go-v1.0+-green.svg?style=flat)](https://github.com/h2non/gentleman)
|
|
||||||
|
|
||||||
Small and dependency free [Go](https://golang.org) package to infer file and MIME type checking the [magic numbers](<https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files>) signature.
|
|
||||||
|
|
||||||
For SVG file type checking, see [go-is-svg](https://github.com/h2non/go-is-svg) package. Python port: [filetype.py](https://github.com/h2non/filetype.py).
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Supports a [wide range](#supported-types) of file types
|
|
||||||
- Provides file extension and proper MIME type
|
|
||||||
- File discovery by extension or MIME type
|
|
||||||
- File discovery by class (image, video, audio...)
|
|
||||||
- Provides a bunch of helpers and file matching shortcuts
|
|
||||||
- [Pluggable](#add-additional-file-type-matchers): add custom new types and matchers
|
|
||||||
- Simple and semantic API
|
|
||||||
- [Blazing fast](#benchmarks), even processing large files
|
|
||||||
- Only first 262 bytes representing the max file header is required, so you can just [pass a slice](#file-header)
|
|
||||||
- Dependency free (just Go code, no C compilation needed)
|
|
||||||
- Cross-platform file recognition
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/h2non/filetype
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
See [Godoc](https://godoc.org/github.com/h2non/filetype) reference.
|
|
||||||
|
|
||||||
### Subpackages
|
|
||||||
|
|
||||||
- [`github.com/h2non/filetype/types`](https://godoc.org/github.com/h2non/filetype/types)
|
|
||||||
- [`github.com/h2non/filetype/matchers`](https://godoc.org/github.com/h2non/filetype/matchers)
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
#### Simple file type checking
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
buf, _ := ioutil.ReadFile("sample.jpg")
|
|
||||||
|
|
||||||
kind, _ := filetype.Match(buf)
|
|
||||||
if kind == filetype.Unknown {
|
|
||||||
fmt.Println("Unknown file type")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("File type: %s. MIME: %s\n", kind.Extension, kind.MIME.Value)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Check type class
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
buf, _ := ioutil.ReadFile("sample.jpg")
|
|
||||||
|
|
||||||
if filetype.IsImage(buf) {
|
|
||||||
fmt.Println("File is an image")
|
|
||||||
} else {
|
|
||||||
fmt.Println("Not an image")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Supported type
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Check if file is supported by extension
|
|
||||||
if filetype.IsSupported("jpg") {
|
|
||||||
fmt.Println("Extension supported")
|
|
||||||
} else {
|
|
||||||
fmt.Println("Extension not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if file is supported by extension
|
|
||||||
if filetype.IsMIMESupported("image/jpeg") {
|
|
||||||
fmt.Println("MIME type supported")
|
|
||||||
} else {
|
|
||||||
fmt.Println("MIME type not supported")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### File header
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Open a file descriptor
|
|
||||||
file, _ := os.Open("movie.mp4")
|
|
||||||
|
|
||||||
// We only have to pass the file header = first 261 bytes
|
|
||||||
head := make([]byte, 261)
|
|
||||||
file.Read(head)
|
|
||||||
|
|
||||||
if filetype.IsImage(head) {
|
|
||||||
fmt.Println("File is an image")
|
|
||||||
} else {
|
|
||||||
fmt.Println("Not an image")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Add additional file type matchers
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
|
||||||
)
|
|
||||||
|
|
||||||
var fooType = filetype.NewType("foo", "foo/foo")
|
|
||||||
|
|
||||||
func fooMatcher(buf []byte) bool {
|
|
||||||
return len(buf) > 1 && buf[0] == 0x01 && buf[1] == 0x02
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Register the new matcher and its type
|
|
||||||
filetype.AddMatcher(fooType, fooMatcher)
|
|
||||||
|
|
||||||
// Check if the new type is supported by extension
|
|
||||||
if filetype.IsSupported("foo") {
|
|
||||||
fmt.Println("New supported type: foo")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the new type is supported by MIME
|
|
||||||
if filetype.IsMIMESupported("foo/foo") {
|
|
||||||
fmt.Println("New supported MIME type: foo/foo")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to match the file
|
|
||||||
fooFile := []byte{0x01, 0x02}
|
|
||||||
kind, _ := filetype.Match(fooFile)
|
|
||||||
if kind == filetype.Unknown {
|
|
||||||
fmt.Println("Unknown file type")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("File type matched: %s\n", kind.Extension)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported types
|
|
||||||
|
|
||||||
#### Image
|
|
||||||
|
|
||||||
- **jpg** - `image/jpeg`
|
|
||||||
- **png** - `image/png`
|
|
||||||
- **gif** - `image/gif`
|
|
||||||
- **webp** - `image/webp`
|
|
||||||
- **cr2** - `image/x-canon-cr2`
|
|
||||||
- **tif** - `image/tiff`
|
|
||||||
- **bmp** - `image/bmp`
|
|
||||||
- **heif** - `image/heif`
|
|
||||||
- **jxr** - `image/vnd.ms-photo`
|
|
||||||
- **psd** - `image/vnd.adobe.photoshop`
|
|
||||||
- **ico** - `image/vnd.microsoft.icon`
|
|
||||||
- **dwg** - `image/vnd.dwg`
|
|
||||||
|
|
||||||
#### Video
|
|
||||||
|
|
||||||
- **mp4** - `video/mp4`
|
|
||||||
- **m4v** - `video/x-m4v`
|
|
||||||
- **mkv** - `video/x-matroska`
|
|
||||||
- **webm** - `video/webm`
|
|
||||||
- **mov** - `video/quicktime`
|
|
||||||
- **avi** - `video/x-msvideo`
|
|
||||||
- **wmv** - `video/x-ms-wmv`
|
|
||||||
- **mpg** - `video/mpeg`
|
|
||||||
- **flv** - `video/x-flv`
|
|
||||||
- **3gp** - `video/3gpp`
|
|
||||||
|
|
||||||
#### Audio
|
|
||||||
|
|
||||||
- **mid** - `audio/midi`
|
|
||||||
- **mp3** - `audio/mpeg`
|
|
||||||
- **m4a** - `audio/m4a`
|
|
||||||
- **ogg** - `audio/ogg`
|
|
||||||
- **flac** - `audio/x-flac`
|
|
||||||
- **wav** - `audio/x-wav`
|
|
||||||
- **amr** - `audio/amr`
|
|
||||||
- **aac** - `audio/aac`
|
|
||||||
- **aiff** - `audio/x-aiff`
|
|
||||||
|
|
||||||
#### Archive
|
|
||||||
|
|
||||||
- **epub** - `application/epub+zip`
|
|
||||||
- **zip** - `application/zip`
|
|
||||||
- **tar** - `application/x-tar`
|
|
||||||
- **rar** - `application/vnd.rar`
|
|
||||||
- **gz** - `application/gzip`
|
|
||||||
- **bz2** - `application/x-bzip2`
|
|
||||||
- **7z** - `application/x-7z-compressed`
|
|
||||||
- **xz** - `application/x-xz`
|
|
||||||
- **zstd** - `application/zstd`
|
|
||||||
- **pdf** - `application/pdf`
|
|
||||||
- **exe** - `application/vnd.microsoft.portable-executable`
|
|
||||||
- **swf** - `application/x-shockwave-flash`
|
|
||||||
- **rtf** - `application/rtf`
|
|
||||||
- **iso** - `application/x-iso9660-image`
|
|
||||||
- **eot** - `application/octet-stream`
|
|
||||||
- **ps** - `application/postscript`
|
|
||||||
- **sqlite** - `application/vnd.sqlite3`
|
|
||||||
- **nes** - `application/x-nintendo-nes-rom`
|
|
||||||
- **crx** - `application/x-google-chrome-extension`
|
|
||||||
- **cab** - `application/vnd.ms-cab-compressed`
|
|
||||||
- **deb** - `application/vnd.debian.binary-package`
|
|
||||||
- **ar** - `application/x-unix-archive`
|
|
||||||
- **Z** - `application/x-compress`
|
|
||||||
- **lz** - `application/x-lzip`
|
|
||||||
- **rpm** - `application/x-rpm`
|
|
||||||
- **elf** - `application/x-executable`
|
|
||||||
- **dcm** - `application/dicom`
|
|
||||||
|
|
||||||
#### Documents
|
|
||||||
|
|
||||||
- **doc** - `application/msword`
|
|
||||||
- **docx** - `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
|
|
||||||
- **xls** - `application/vnd.ms-excel`
|
|
||||||
- **xlsx** - `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
|
|
||||||
- **ppt** - `application/vnd.ms-powerpoint`
|
|
||||||
- **pptx** - `application/vnd.openxmlformats-officedocument.presentationml.presentation`
|
|
||||||
|
|
||||||
#### Font
|
|
||||||
|
|
||||||
- **woff** - `application/font-woff`
|
|
||||||
- **woff2** - `application/font-woff`
|
|
||||||
- **ttf** - `application/font-sfnt`
|
|
||||||
- **otf** - `application/font-sfnt`
|
|
||||||
|
|
||||||
#### Application
|
|
||||||
|
|
||||||
- **wasm** - `application/wasm`
|
|
||||||
- **dex** - `application/vnd.android.dex`
|
|
||||||
- **dey** - `application/vnd.android.dey`
|
|
||||||
|
|
||||||
## Benchmarks
|
|
||||||
|
|
||||||
Measured using [real files](https://github.com/h2non/filetype/tree/master/fixtures).
|
|
||||||
|
|
||||||
Environment: OSX x64 i7 2.7 Ghz
|
|
||||||
|
|
||||||
```bash
|
|
||||||
BenchmarkMatchTar-8 1000000 1083 ns/op
|
|
||||||
BenchmarkMatchZip-8 1000000 1162 ns/op
|
|
||||||
BenchmarkMatchJpeg-8 1000000 1280 ns/op
|
|
||||||
BenchmarkMatchGif-8 1000000 1315 ns/op
|
|
||||||
BenchmarkMatchPng-8 1000000 1121 ns/op
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT - Tomas Aparicio
|
|
|
@ -1,102 +0,0 @@
|
||||||
package filetype
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/h2non/filetype/matchers"
|
|
||||||
"github.com/h2non/filetype/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Types stores a map of supported types
|
|
||||||
var Types = types.Types
|
|
||||||
|
|
||||||
// NewType creates and registers a new type
|
|
||||||
var NewType = types.NewType
|
|
||||||
|
|
||||||
// Unknown represents an unknown file type
|
|
||||||
var Unknown = types.Unknown
|
|
||||||
|
|
||||||
// ErrEmptyBuffer represents an empty buffer error
|
|
||||||
var ErrEmptyBuffer = errors.New("Empty buffer")
|
|
||||||
|
|
||||||
// ErrUnknownBuffer represents a unknown buffer error
|
|
||||||
var ErrUnknownBuffer = errors.New("Unknown buffer type")
|
|
||||||
|
|
||||||
// AddType registers a new file type
|
|
||||||
func AddType(ext, mime string) types.Type {
|
|
||||||
return types.NewType(ext, mime)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is checks if a given buffer matches with the given file type extension
|
|
||||||
func Is(buf []byte, ext string) bool {
|
|
||||||
kind := types.Get(ext)
|
|
||||||
if kind != types.Unknown {
|
|
||||||
return IsType(buf, kind)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsExtension semantic alias to Is()
|
|
||||||
func IsExtension(buf []byte, ext string) bool {
|
|
||||||
return Is(buf, ext)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsType checks if a given buffer matches with the given file type
|
|
||||||
func IsType(buf []byte, kind types.Type) bool {
|
|
||||||
matcher := matchers.Matchers[kind]
|
|
||||||
if matcher == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return matcher(buf) != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsMIME checks if a given buffer matches with the given MIME type
|
|
||||||
func IsMIME(buf []byte, mime string) bool {
|
|
||||||
result := false
|
|
||||||
types.Types.Range(func(k, v interface{}) bool {
|
|
||||||
kind := v.(types.Type)
|
|
||||||
if kind.MIME.Value == mime {
|
|
||||||
matcher := matchers.Matchers[kind]
|
|
||||||
result = matcher(buf) != types.Unknown
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSupported checks if a given file extension is supported
|
|
||||||
func IsSupported(ext string) bool {
|
|
||||||
result := false
|
|
||||||
types.Types.Range(func(k, v interface{}) bool {
|
|
||||||
key := k.(string)
|
|
||||||
if key == ext {
|
|
||||||
result = true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsMIMESupported checks if a given MIME type is supported
|
|
||||||
func IsMIMESupported(mime string) bool {
|
|
||||||
result := false
|
|
||||||
types.Types.Range(func(k, v interface{}) bool {
|
|
||||||
kind := v.(types.Type)
|
|
||||||
if kind.MIME.Value == mime {
|
|
||||||
result = true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetType retrieves a Type by file extension
|
|
||||||
func GetType(ext string) types.Type {
|
|
||||||
return types.Get(ext)
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
package filetype
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/h2non/filetype/matchers"
|
|
||||||
"github.com/h2non/filetype/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Image tries to match a file as image type
|
|
||||||
func Image(buf []byte) (types.Type, error) {
|
|
||||||
return doMatchMap(buf, matchers.Image)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsImage checks if the given buffer is an image type
|
|
||||||
func IsImage(buf []byte) bool {
|
|
||||||
kind, _ := Image(buf)
|
|
||||||
return kind != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// Audio tries to match a file as audio type
|
|
||||||
func Audio(buf []byte) (types.Type, error) {
|
|
||||||
return doMatchMap(buf, matchers.Audio)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAudio checks if the given buffer is an audio type
|
|
||||||
func IsAudio(buf []byte) bool {
|
|
||||||
kind, _ := Audio(buf)
|
|
||||||
return kind != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// Video tries to match a file as video type
|
|
||||||
func Video(buf []byte) (types.Type, error) {
|
|
||||||
return doMatchMap(buf, matchers.Video)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsVideo checks if the given buffer is a video type
|
|
||||||
func IsVideo(buf []byte) bool {
|
|
||||||
kind, _ := Video(buf)
|
|
||||||
return kind != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// Font tries to match a file as text font type
|
|
||||||
func Font(buf []byte) (types.Type, error) {
|
|
||||||
return doMatchMap(buf, matchers.Font)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFont checks if the given buffer is a font type
|
|
||||||
func IsFont(buf []byte) bool {
|
|
||||||
kind, _ := Font(buf)
|
|
||||||
return kind != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// Archive tries to match a file as generic archive type
|
|
||||||
func Archive(buf []byte) (types.Type, error) {
|
|
||||||
return doMatchMap(buf, matchers.Archive)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsArchive checks if the given buffer is an archive type
|
|
||||||
func IsArchive(buf []byte) bool {
|
|
||||||
kind, _ := Archive(buf)
|
|
||||||
return kind != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// Document tries to match a file as document type
|
|
||||||
func Document(buf []byte) (types.Type, error) {
|
|
||||||
return doMatchMap(buf, matchers.Document)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDocument checks if the given buffer is an document type
|
|
||||||
func IsDocument(buf []byte) bool {
|
|
||||||
kind, _ := Document(buf)
|
|
||||||
return kind != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application tries to match a file as an application type
|
|
||||||
func Application(buf []byte) (types.Type, error) {
|
|
||||||
return doMatchMap(buf, matchers.Application)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsApplication checks if the given buffer is an application type
|
|
||||||
func IsApplication(buf []byte) bool {
|
|
||||||
kind, _ := Application(buf)
|
|
||||||
return kind != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
func doMatchMap(buf []byte, machers matchers.Map) (types.Type, error) {
|
|
||||||
kind := MatchMap(buf, machers)
|
|
||||||
if kind != types.Unknown {
|
|
||||||
return kind, nil
|
|
||||||
}
|
|
||||||
return kind, ErrUnknownBuffer
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
package filetype
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/h2non/filetype/matchers"
|
|
||||||
"github.com/h2non/filetype/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Matchers is an alias to matchers.Matchers
|
|
||||||
var Matchers = matchers.Matchers
|
|
||||||
|
|
||||||
// MatcherKeys is an alias to matchers.MatcherKeys
|
|
||||||
var MatcherKeys = &matchers.MatcherKeys
|
|
||||||
|
|
||||||
// NewMatcher is an alias to matchers.NewMatcher
|
|
||||||
var NewMatcher = matchers.NewMatcher
|
|
||||||
|
|
||||||
// Match infers the file type of a given buffer inspecting its magic numbers signature
|
|
||||||
func Match(buf []byte) (types.Type, error) {
|
|
||||||
length := len(buf)
|
|
||||||
if length == 0 {
|
|
||||||
return types.Unknown, ErrEmptyBuffer
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, kind := range *MatcherKeys {
|
|
||||||
checker := Matchers[kind]
|
|
||||||
match := checker(buf)
|
|
||||||
if match != types.Unknown && match.Extension != "" {
|
|
||||||
return match, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.Unknown, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get is an alias to Match()
|
|
||||||
func Get(buf []byte) (types.Type, error) {
|
|
||||||
return Match(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchFile infers a file type for a file
|
|
||||||
func MatchFile(filepath string) (types.Type, error) {
|
|
||||||
file, err := os.Open(filepath)
|
|
||||||
if err != nil {
|
|
||||||
return types.Unknown, err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
return MatchReader(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchReader is convenient wrapper to Match() any Reader
|
|
||||||
func MatchReader(reader io.Reader) (types.Type, error) {
|
|
||||||
buffer := make([]byte, 8192) // 8K makes msooxml tests happy and allows for expanded custom file checks
|
|
||||||
|
|
||||||
_, err := reader.Read(buffer)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return types.Unknown, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Match(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMatcher registers a new matcher type
|
|
||||||
func AddMatcher(fileType types.Type, matcher matchers.Matcher) matchers.TypeMatcher {
|
|
||||||
return matchers.NewMatcher(fileType, matcher)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches checks if the given buffer matches with some supported file type
|
|
||||||
func Matches(buf []byte) bool {
|
|
||||||
kind, _ := Match(buf)
|
|
||||||
return kind != types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchMap performs a file matching against a map of match functions
|
|
||||||
func MatchMap(buf []byte, matchers matchers.Map) types.Type {
|
|
||||||
for kind, matcher := range matchers {
|
|
||||||
if matcher(buf) {
|
|
||||||
return kind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchesMap is an alias to Matches() but using matching against a map of match functions
|
|
||||||
func MatchesMap(buf []byte, matchers matchers.Map) bool {
|
|
||||||
return MatchMap(buf, matchers) != types.Unknown
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package matchers
|
|
||||||
|
|
||||||
var (
|
|
||||||
TypeWasm = newType("wasm", "application/wasm")
|
|
||||||
TypeDex = newType("dex", "application/vnd.android.dex")
|
|
||||||
TypeDey = newType("dey", "application/vnd.android.dey")
|
|
||||||
)
|
|
||||||
|
|
||||||
var Application = Map{
|
|
||||||
TypeWasm: Wasm,
|
|
||||||
TypeDex: Dex,
|
|
||||||
TypeDey: Dey,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wasm detects a Web Assembly 1.0 filetype.
|
|
||||||
func Wasm(buf []byte) bool {
|
|
||||||
// WASM has starts with `\0asm`, followed by the version.
|
|
||||||
// http://webassembly.github.io/spec/core/binary/modules.html#binary-magic
|
|
||||||
return len(buf) >= 8 &&
|
|
||||||
buf[0] == 0x00 && buf[1] == 0x61 &&
|
|
||||||
buf[2] == 0x73 && buf[3] == 0x6D &&
|
|
||||||
buf[4] == 0x01 && buf[5] == 0x00 &&
|
|
||||||
buf[6] == 0x00 && buf[7] == 0x00
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dex detects dalvik executable(DEX)
|
|
||||||
func Dex(buf []byte) bool {
|
|
||||||
// https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
|
|
||||||
return len(buf) > 36 &&
|
|
||||||
// magic
|
|
||||||
buf[0] == 0x64 && buf[1] == 0x65 && buf[2] == 0x78 && buf[3] == 0x0A &&
|
|
||||||
// file sise
|
|
||||||
buf[36] == 0x70
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dey Optimized Dalvik Executable(ODEX)
|
|
||||||
func Dey(buf []byte) bool {
|
|
||||||
return len(buf) > 100 &&
|
|
||||||
// dey magic
|
|
||||||
buf[0] == 0x64 && buf[1] == 0x65 && buf[2] == 0x79 && buf[3] == 0x0A &&
|
|
||||||
// dex
|
|
||||||
Dex(buf[40:100])
|
|
||||||
}
|
|
|
@ -1,211 +0,0 @@
|
||||||
package matchers
|
|
||||||
|
|
||||||
import "encoding/binary"
|
|
||||||
|
|
||||||
const (
|
|
||||||
ZstdMagicSkippableStart = 0x184D2A50
|
|
||||||
ZstdMagicSkippableMask = 0xFFFFFFF0
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
TypeEpub = newType("epub", "application/epub+zip")
|
|
||||||
TypeZip = newType("zip", "application/zip")
|
|
||||||
TypeTar = newType("tar", "application/x-tar")
|
|
||||||
TypeRar = newType("rar", "application/vnd.rar")
|
|
||||||
TypeGz = newType("gz", "application/gzip")
|
|
||||||
TypeBz2 = newType("bz2", "application/x-bzip2")
|
|
||||||
Type7z = newType("7z", "application/x-7z-compressed")
|
|
||||||
TypeXz = newType("xz", "application/x-xz")
|
|
||||||
TypeZstd = newType("zst", "application/zstd")
|
|
||||||
TypePdf = newType("pdf", "application/pdf")
|
|
||||||
TypeExe = newType("exe", "application/vnd.microsoft.portable-executable")
|
|
||||||
TypeSwf = newType("swf", "application/x-shockwave-flash")
|
|
||||||
TypeRtf = newType("rtf", "application/rtf")
|
|
||||||
TypeEot = newType("eot", "application/octet-stream")
|
|
||||||
TypePs = newType("ps", "application/postscript")
|
|
||||||
TypeSqlite = newType("sqlite", "application/vnd.sqlite3")
|
|
||||||
TypeNes = newType("nes", "application/x-nintendo-nes-rom")
|
|
||||||
TypeCrx = newType("crx", "application/x-google-chrome-extension")
|
|
||||||
TypeCab = newType("cab", "application/vnd.ms-cab-compressed")
|
|
||||||
TypeDeb = newType("deb", "application/vnd.debian.binary-package")
|
|
||||||
TypeAr = newType("ar", "application/x-unix-archive")
|
|
||||||
TypeZ = newType("Z", "application/x-compress")
|
|
||||||
TypeLz = newType("lz", "application/x-lzip")
|
|
||||||
TypeRpm = newType("rpm", "application/x-rpm")
|
|
||||||
TypeElf = newType("elf", "application/x-executable")
|
|
||||||
TypeDcm = newType("dcm", "application/dicom")
|
|
||||||
TypeIso = newType("iso", "application/x-iso9660-image")
|
|
||||||
TypeMachO = newType("macho", "application/x-mach-binary") // Mach-O binaries have no common extension.
|
|
||||||
)
|
|
||||||
|
|
||||||
var Archive = Map{
|
|
||||||
TypeEpub: bytePrefixMatcher(epubMagic),
|
|
||||||
TypeZip: Zip,
|
|
||||||
TypeTar: Tar,
|
|
||||||
TypeRar: Rar,
|
|
||||||
TypeGz: bytePrefixMatcher(gzMagic),
|
|
||||||
TypeBz2: bytePrefixMatcher(bz2Magic),
|
|
||||||
Type7z: bytePrefixMatcher(sevenzMagic),
|
|
||||||
TypeXz: bytePrefixMatcher(xzMagic),
|
|
||||||
TypeZstd: Zst,
|
|
||||||
TypePdf: bytePrefixMatcher(pdfMagic),
|
|
||||||
TypeExe: bytePrefixMatcher(exeMagic),
|
|
||||||
TypeSwf: Swf,
|
|
||||||
TypeRtf: bytePrefixMatcher(rtfMagic),
|
|
||||||
TypeEot: Eot,
|
|
||||||
TypePs: bytePrefixMatcher(psMagic),
|
|
||||||
TypeSqlite: bytePrefixMatcher(sqliteMagic),
|
|
||||||
TypeNes: bytePrefixMatcher(nesMagic),
|
|
||||||
TypeCrx: bytePrefixMatcher(crxMagic),
|
|
||||||
TypeCab: Cab,
|
|
||||||
TypeDeb: bytePrefixMatcher(debMagic),
|
|
||||||
TypeAr: bytePrefixMatcher(arMagic),
|
|
||||||
TypeZ: Z,
|
|
||||||
TypeLz: bytePrefixMatcher(lzMagic),
|
|
||||||
TypeRpm: Rpm,
|
|
||||||
TypeElf: Elf,
|
|
||||||
TypeDcm: Dcm,
|
|
||||||
TypeIso: Iso,
|
|
||||||
TypeMachO: MachO,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
epubMagic = []byte{
|
|
||||||
0x50, 0x4B, 0x03, 0x04, 0x6D, 0x69, 0x6D, 0x65,
|
|
||||||
0x74, 0x79, 0x70, 0x65, 0x61, 0x70, 0x70, 0x6C,
|
|
||||||
0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F,
|
|
||||||
0x65, 0x70, 0x75, 0x62, 0x2B, 0x7A, 0x69, 0x70,
|
|
||||||
}
|
|
||||||
gzMagic = []byte{0x1F, 0x8B, 0x08}
|
|
||||||
bz2Magic = []byte{0x42, 0x5A, 0x68}
|
|
||||||
sevenzMagic = []byte{0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C}
|
|
||||||
pdfMagic = []byte{0x25, 0x50, 0x44, 0x46}
|
|
||||||
exeMagic = []byte{0x4D, 0x5A}
|
|
||||||
rtfMagic = []byte{0x7B, 0x5C, 0x72, 0x74, 0x66}
|
|
||||||
nesMagic = []byte{0x4E, 0x45, 0x53, 0x1A}
|
|
||||||
crxMagic = []byte{0x43, 0x72, 0x32, 0x34}
|
|
||||||
psMagic = []byte{0x25, 0x21}
|
|
||||||
xzMagic = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
|
|
||||||
sqliteMagic = []byte{0x53, 0x51, 0x4C, 0x69}
|
|
||||||
debMagic = []byte{
|
|
||||||
0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A,
|
|
||||||
0x64, 0x65, 0x62, 0x69, 0x61, 0x6E, 0x2D, 0x62,
|
|
||||||
0x69, 0x6E, 0x61, 0x72, 0x79,
|
|
||||||
}
|
|
||||||
arMagic = []byte{0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E}
|
|
||||||
zstdMagic = []byte{0x28, 0xB5, 0x2F, 0xFD}
|
|
||||||
lzMagic = []byte{0x4C, 0x5A, 0x49, 0x50}
|
|
||||||
)
|
|
||||||
|
|
||||||
func bytePrefixMatcher(magicPattern []byte) Matcher {
|
|
||||||
return func(data []byte) bool {
|
|
||||||
return compareBytes(data, magicPattern, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Zip(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x50 && buf[1] == 0x4B &&
|
|
||||||
(buf[2] == 0x3 || buf[2] == 0x5 || buf[2] == 0x7) &&
|
|
||||||
(buf[3] == 0x4 || buf[3] == 0x6 || buf[3] == 0x8)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Tar(buf []byte) bool {
|
|
||||||
return len(buf) > 261 &&
|
|
||||||
buf[257] == 0x75 && buf[258] == 0x73 &&
|
|
||||||
buf[259] == 0x74 && buf[260] == 0x61 &&
|
|
||||||
buf[261] == 0x72
|
|
||||||
}
|
|
||||||
|
|
||||||
func Rar(buf []byte) bool {
|
|
||||||
return len(buf) > 6 &&
|
|
||||||
buf[0] == 0x52 && buf[1] == 0x61 && buf[2] == 0x72 &&
|
|
||||||
buf[3] == 0x21 && buf[4] == 0x1A && buf[5] == 0x7 &&
|
|
||||||
(buf[6] == 0x0 || buf[6] == 0x1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Swf(buf []byte) bool {
|
|
||||||
return len(buf) > 2 &&
|
|
||||||
(buf[0] == 0x43 || buf[0] == 0x46) &&
|
|
||||||
buf[1] == 0x57 && buf[2] == 0x53
|
|
||||||
}
|
|
||||||
|
|
||||||
func Cab(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
((buf[0] == 0x4D && buf[1] == 0x53 && buf[2] == 0x43 && buf[3] == 0x46) ||
|
|
||||||
(buf[0] == 0x49 && buf[1] == 0x53 && buf[2] == 0x63 && buf[3] == 0x28))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Eot(buf []byte) bool {
|
|
||||||
return len(buf) > 35 &&
|
|
||||||
buf[34] == 0x4C && buf[35] == 0x50 &&
|
|
||||||
((buf[8] == 0x02 && buf[9] == 0x00 &&
|
|
||||||
buf[10] == 0x01) || (buf[8] == 0x01 &&
|
|
||||||
buf[9] == 0x00 && buf[10] == 0x00) ||
|
|
||||||
(buf[8] == 0x02 && buf[9] == 0x00 &&
|
|
||||||
buf[10] == 0x02))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Z(buf []byte) bool {
|
|
||||||
return len(buf) > 1 &&
|
|
||||||
((buf[0] == 0x1F && buf[1] == 0xA0) ||
|
|
||||||
(buf[0] == 0x1F && buf[1] == 0x9D))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Rpm(buf []byte) bool {
|
|
||||||
return len(buf) > 96 &&
|
|
||||||
buf[0] == 0xED && buf[1] == 0xAB &&
|
|
||||||
buf[2] == 0xEE && buf[3] == 0xDB
|
|
||||||
}
|
|
||||||
|
|
||||||
func Elf(buf []byte) bool {
|
|
||||||
return len(buf) > 52 &&
|
|
||||||
buf[0] == 0x7F && buf[1] == 0x45 &&
|
|
||||||
buf[2] == 0x4C && buf[3] == 0x46
|
|
||||||
}
|
|
||||||
|
|
||||||
func Dcm(buf []byte) bool {
|
|
||||||
return len(buf) > 131 &&
|
|
||||||
buf[128] == 0x44 && buf[129] == 0x49 &&
|
|
||||||
buf[130] == 0x43 && buf[131] == 0x4D
|
|
||||||
}
|
|
||||||
|
|
||||||
func Iso(buf []byte) bool {
|
|
||||||
return len(buf) > 32773 &&
|
|
||||||
buf[32769] == 0x43 && buf[32770] == 0x44 &&
|
|
||||||
buf[32771] == 0x30 && buf[32772] == 0x30 &&
|
|
||||||
buf[32773] == 0x31
|
|
||||||
}
|
|
||||||
|
|
||||||
func MachO(buf []byte) bool {
|
|
||||||
return len(buf) > 3 && ((buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && buf[3] == 0xCF) ||
|
|
||||||
(buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && buf[3] == 0xCE) ||
|
|
||||||
(buf[0] == 0xBE && buf[1] == 0xBA && buf[2] == 0xFE && buf[3] == 0xCA) ||
|
|
||||||
// Big endian versions below here...
|
|
||||||
(buf[0] == 0xCF && buf[1] == 0xFA && buf[2] == 0xED && buf[3] == 0xFE) ||
|
|
||||||
(buf[0] == 0xCE && buf[1] == 0xFA && buf[2] == 0xED && buf[3] == 0xFE) ||
|
|
||||||
(buf[0] == 0xCA && buf[1] == 0xFE && buf[2] == 0xBA && buf[3] == 0xBE))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zstandard compressed data is made of one or more frames.
|
|
||||||
// There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames.
|
|
||||||
// See more details from https://tools.ietf.org/id/draft-kucherawy-dispatch-zstd-00.html#rfc.section.2
|
|
||||||
func Zst(buf []byte) bool {
|
|
||||||
if compareBytes(buf, zstdMagic, 0) {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
// skippable frames
|
|
||||||
if len(buf) < 8 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if binary.LittleEndian.Uint32(buf[:4]) & ZstdMagicSkippableMask == ZstdMagicSkippableStart {
|
|
||||||
userDataLength := binary.LittleEndian.Uint32(buf[4:8])
|
|
||||||
if len(buf) < 8 + int(userDataLength) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
nextFrame := buf[8+userDataLength:]
|
|
||||||
return Zst(nextFrame)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
package matchers
|
|
||||||
|
|
||||||
var (
|
|
||||||
TypeMidi = newType("mid", "audio/midi")
|
|
||||||
TypeMp3 = newType("mp3", "audio/mpeg")
|
|
||||||
TypeM4a = newType("m4a", "audio/m4a")
|
|
||||||
TypeOgg = newType("ogg", "audio/ogg")
|
|
||||||
TypeFlac = newType("flac", "audio/x-flac")
|
|
||||||
TypeWav = newType("wav", "audio/x-wav")
|
|
||||||
TypeAmr = newType("amr", "audio/amr")
|
|
||||||
TypeAac = newType("aac", "audio/aac")
|
|
||||||
TypeAiff = newType("aiff", "audio/x-aiff")
|
|
||||||
)
|
|
||||||
|
|
||||||
var Audio = Map{
|
|
||||||
TypeMidi: Midi,
|
|
||||||
TypeMp3: Mp3,
|
|
||||||
TypeM4a: M4a,
|
|
||||||
TypeOgg: Ogg,
|
|
||||||
TypeFlac: Flac,
|
|
||||||
TypeWav: Wav,
|
|
||||||
TypeAmr: Amr,
|
|
||||||
TypeAac: Aac,
|
|
||||||
TypeAiff: Aiff,
|
|
||||||
}
|
|
||||||
|
|
||||||
func Midi(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x4D && buf[1] == 0x54 &&
|
|
||||||
buf[2] == 0x68 && buf[3] == 0x64
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mp3(buf []byte) bool {
|
|
||||||
return len(buf) > 2 &&
|
|
||||||
((buf[0] == 0x49 && buf[1] == 0x44 && buf[2] == 0x33) ||
|
|
||||||
(buf[0] == 0xFF && buf[1] == 0xfb))
|
|
||||||
}
|
|
||||||
|
|
||||||
func M4a(buf []byte) bool {
|
|
||||||
return len(buf) > 10 &&
|
|
||||||
((buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 &&
|
|
||||||
buf[7] == 0x70 && buf[8] == 0x4D && buf[9] == 0x34 && buf[10] == 0x41) ||
|
|
||||||
(buf[0] == 0x4D && buf[1] == 0x34 && buf[2] == 0x41 && buf[3] == 0x20))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ogg(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x4F && buf[1] == 0x67 &&
|
|
||||||
buf[2] == 0x67 && buf[3] == 0x53
|
|
||||||
}
|
|
||||||
|
|
||||||
func Flac(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x66 && buf[1] == 0x4C &&
|
|
||||||
buf[2] == 0x61 && buf[3] == 0x43
|
|
||||||
}
|
|
||||||
|
|
||||||
func Wav(buf []byte) bool {
|
|
||||||
return len(buf) > 11 &&
|
|
||||||
buf[0] == 0x52 && buf[1] == 0x49 &&
|
|
||||||
buf[2] == 0x46 && buf[3] == 0x46 &&
|
|
||||||
buf[8] == 0x57 && buf[9] == 0x41 &&
|
|
||||||
buf[10] == 0x56 && buf[11] == 0x45
|
|
||||||
}
|
|
||||||
|
|
||||||
func Amr(buf []byte) bool {
|
|
||||||
return len(buf) > 11 &&
|
|
||||||
buf[0] == 0x23 && buf[1] == 0x21 &&
|
|
||||||
buf[2] == 0x41 && buf[3] == 0x4D &&
|
|
||||||
buf[4] == 0x52 && buf[5] == 0x0A
|
|
||||||
}
|
|
||||||
|
|
||||||
func Aac(buf []byte) bool {
|
|
||||||
return len(buf) > 1 &&
|
|
||||||
((buf[0] == 0xFF && buf[1] == 0xF1) ||
|
|
||||||
(buf[0] == 0xFF && buf[1] == 0xF9))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Aiff(buf []byte) bool {
|
|
||||||
return len(buf) > 11 &&
|
|
||||||
buf[0] == 0x46 && buf[1] == 0x4F &&
|
|
||||||
buf[2] == 0x52 && buf[3] == 0x4D &&
|
|
||||||
buf[8] == 0x41 && buf[9] == 0x49 &&
|
|
||||||
buf[10] == 0x46 && buf[11] == 0x46
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
package matchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
TypeDoc = newType("doc", "application/msword")
|
|
||||||
TypeDocx = newType("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
|
|
||||||
TypeXls = newType("xls", "application/vnd.ms-excel")
|
|
||||||
TypeXlsx = newType("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
|
||||||
TypePpt = newType("ppt", "application/vnd.ms-powerpoint")
|
|
||||||
TypePptx = newType("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation")
|
|
||||||
)
|
|
||||||
|
|
||||||
var Document = Map{
|
|
||||||
TypeDoc: Doc,
|
|
||||||
TypeDocx: Docx,
|
|
||||||
TypeXls: Xls,
|
|
||||||
TypeXlsx: Xlsx,
|
|
||||||
TypePpt: Ppt,
|
|
||||||
TypePptx: Pptx,
|
|
||||||
}
|
|
||||||
|
|
||||||
type docType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
TYPE_DOC docType = iota
|
|
||||||
TYPE_DOCX
|
|
||||||
TYPE_XLS
|
|
||||||
TYPE_XLSX
|
|
||||||
TYPE_PPT
|
|
||||||
TYPE_PPTX
|
|
||||||
TYPE_OOXML
|
|
||||||
)
|
|
||||||
|
|
||||||
//reference: https://bz.apache.org/ooo/show_bug.cgi?id=111457
|
|
||||||
func Doc(buf []byte) bool {
|
|
||||||
if len(buf) > 513 {
|
|
||||||
return buf[0] == 0xD0 && buf[1] == 0xCF &&
|
|
||||||
buf[2] == 0x11 && buf[3] == 0xE0 &&
|
|
||||||
buf[512] == 0xEC && buf[513] == 0xA5
|
|
||||||
} else {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0xD0 && buf[1] == 0xCF &&
|
|
||||||
buf[2] == 0x11 && buf[3] == 0xE0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Docx(buf []byte) bool {
|
|
||||||
typ, ok := msooxml(buf)
|
|
||||||
return ok && typ == TYPE_DOCX
|
|
||||||
}
|
|
||||||
|
|
||||||
func Xls(buf []byte) bool {
|
|
||||||
if len(buf) > 513 {
|
|
||||||
return buf[0] == 0xD0 && buf[1] == 0xCF &&
|
|
||||||
buf[2] == 0x11 && buf[3] == 0xE0 &&
|
|
||||||
buf[512] == 0x09 && buf[513] == 0x08
|
|
||||||
} else {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0xD0 && buf[1] == 0xCF &&
|
|
||||||
buf[2] == 0x11 && buf[3] == 0xE0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Xlsx(buf []byte) bool {
|
|
||||||
typ, ok := msooxml(buf)
|
|
||||||
return ok && typ == TYPE_XLSX
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ppt(buf []byte) bool {
|
|
||||||
if len(buf) > 513 {
|
|
||||||
return buf[0] == 0xD0 && buf[1] == 0xCF &&
|
|
||||||
buf[2] == 0x11 && buf[3] == 0xE0 &&
|
|
||||||
buf[512] == 0xA0 && buf[513] == 0x46
|
|
||||||
} else {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0xD0 && buf[1] == 0xCF &&
|
|
||||||
buf[2] == 0x11 && buf[3] == 0xE0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Pptx(buf []byte) bool {
|
|
||||||
typ, ok := msooxml(buf)
|
|
||||||
return ok && typ == TYPE_PPTX
|
|
||||||
}
|
|
||||||
|
|
||||||
func msooxml(buf []byte) (typ docType, found bool) {
|
|
||||||
signature := []byte{'P', 'K', 0x03, 0x04}
|
|
||||||
|
|
||||||
// start by checking for ZIP local file header signature
|
|
||||||
if ok := compareBytes(buf, signature, 0); !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure the first file is correct
|
|
||||||
if v, ok := checkMSOoml(buf, 0x1E); ok {
|
|
||||||
return v, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
if !compareBytes(buf, []byte("[Content_Types].xml"), 0x1E) &&
|
|
||||||
!compareBytes(buf, []byte("_rels/.rels"), 0x1E) &&
|
|
||||||
!compareBytes(buf, []byte("docProps"), 0x1E) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip to the second local file header
|
|
||||||
// since some documents include a 520-byte extra field following the file
|
|
||||||
// header, we need to scan for the next header
|
|
||||||
startOffset := int(binary.LittleEndian.Uint32(buf[18:22]) + 49)
|
|
||||||
idx := search(buf, startOffset, 6000)
|
|
||||||
if idx == -1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// now skip to the *third* local file header; again, we need to scan due to a
|
|
||||||
// 520-byte extra field following the file header
|
|
||||||
startOffset += idx + 4 + 26
|
|
||||||
idx = search(buf, startOffset, 6000)
|
|
||||||
if idx == -1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// and check the subdirectory name to determine which type of OOXML
|
|
||||||
// file we have. Correct the mimetype with the registered ones:
|
|
||||||
// http://technet.microsoft.com/en-us/library/cc179224.aspx
|
|
||||||
startOffset += idx + 4 + 26
|
|
||||||
if typ, ok := checkMSOoml(buf, startOffset); ok {
|
|
||||||
return typ, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenOffice/Libreoffice orders ZIP entry differently, so check the 4th file
|
|
||||||
startOffset += 26
|
|
||||||
idx = search(buf, startOffset, 6000)
|
|
||||||
if idx == -1 {
|
|
||||||
return TYPE_OOXML, true
|
|
||||||
}
|
|
||||||
|
|
||||||
startOffset += idx + 4 + 26
|
|
||||||
if typ, ok := checkMSOoml(buf, startOffset); ok {
|
|
||||||
return typ, ok
|
|
||||||
} else {
|
|
||||||
return TYPE_OOXML, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareBytes(slice, subSlice []byte, startOffset int) bool {
|
|
||||||
sl := len(subSlice)
|
|
||||||
|
|
||||||
if startOffset+sl > len(slice) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
s := slice[startOffset : startOffset+sl]
|
|
||||||
for i := range s {
|
|
||||||
if subSlice[i] != s[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkMSOoml(buf []byte, offset int) (typ docType, ok bool) {
|
|
||||||
ok = true
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case compareBytes(buf, []byte("word/"), offset):
|
|
||||||
typ = TYPE_DOCX
|
|
||||||
case compareBytes(buf, []byte("ppt/"), offset):
|
|
||||||
typ = TYPE_PPTX
|
|
||||||
case compareBytes(buf, []byte("xl/"), offset):
|
|
||||||
typ = TYPE_XLSX
|
|
||||||
default:
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func search(buf []byte, start, rangeNum int) int {
|
|
||||||
length := len(buf)
|
|
||||||
end := start + rangeNum
|
|
||||||
signature := []byte{'P', 'K', 0x03, 0x04}
|
|
||||||
|
|
||||||
if end > length {
|
|
||||||
end = length
|
|
||||||
}
|
|
||||||
|
|
||||||
if start >= end {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes.Index(buf[start:end], signature)
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package matchers
|
|
||||||
|
|
||||||
var (
|
|
||||||
TypeWoff = newType("woff", "application/font-woff")
|
|
||||||
TypeWoff2 = newType("woff2", "application/font-woff")
|
|
||||||
TypeTtf = newType("ttf", "application/font-sfnt")
|
|
||||||
TypeOtf = newType("otf", "application/font-sfnt")
|
|
||||||
)
|
|
||||||
|
|
||||||
var Font = Map{
|
|
||||||
TypeWoff: Woff,
|
|
||||||
TypeWoff2: Woff2,
|
|
||||||
TypeTtf: Ttf,
|
|
||||||
TypeOtf: Otf,
|
|
||||||
}
|
|
||||||
|
|
||||||
func Woff(buf []byte) bool {
|
|
||||||
return len(buf) > 7 &&
|
|
||||||
buf[0] == 0x77 && buf[1] == 0x4F &&
|
|
||||||
buf[2] == 0x46 && buf[3] == 0x46 &&
|
|
||||||
buf[4] == 0x00 && buf[5] == 0x01 &&
|
|
||||||
buf[6] == 0x00 && buf[7] == 0x00
|
|
||||||
}
|
|
||||||
|
|
||||||
func Woff2(buf []byte) bool {
|
|
||||||
return len(buf) > 7 &&
|
|
||||||
buf[0] == 0x77 && buf[1] == 0x4F &&
|
|
||||||
buf[2] == 0x46 && buf[3] == 0x32 &&
|
|
||||||
buf[4] == 0x00 && buf[5] == 0x01 &&
|
|
||||||
buf[6] == 0x00 && buf[7] == 0x00
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ttf(buf []byte) bool {
|
|
||||||
return len(buf) > 4 &&
|
|
||||||
buf[0] == 0x00 && buf[1] == 0x01 &&
|
|
||||||
buf[2] == 0x00 && buf[3] == 0x00 &&
|
|
||||||
buf[4] == 0x00
|
|
||||||
}
|
|
||||||
|
|
||||||
func Otf(buf []byte) bool {
|
|
||||||
return len(buf) > 4 &&
|
|
||||||
buf[0] == 0x4F && buf[1] == 0x54 &&
|
|
||||||
buf[2] == 0x54 && buf[3] == 0x4F &&
|
|
||||||
buf[4] == 0x00
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
package matchers
|
|
||||||
|
|
||||||
import "github.com/h2non/filetype/matchers/isobmff"
|
|
||||||
|
|
||||||
var (
|
|
||||||
TypeJpeg = newType("jpg", "image/jpeg")
|
|
||||||
TypeJpeg2000 = newType("jp2", "image/jp2")
|
|
||||||
TypePng = newType("png", "image/png")
|
|
||||||
TypeGif = newType("gif", "image/gif")
|
|
||||||
TypeWebp = newType("webp", "image/webp")
|
|
||||||
TypeCR2 = newType("cr2", "image/x-canon-cr2")
|
|
||||||
TypeTiff = newType("tif", "image/tiff")
|
|
||||||
TypeBmp = newType("bmp", "image/bmp")
|
|
||||||
TypeJxr = newType("jxr", "image/vnd.ms-photo")
|
|
||||||
TypePsd = newType("psd", "image/vnd.adobe.photoshop")
|
|
||||||
TypeIco = newType("ico", "image/vnd.microsoft.icon")
|
|
||||||
TypeHeif = newType("heif", "image/heif")
|
|
||||||
TypeDwg = newType("dwg", "image/vnd.dwg")
|
|
||||||
)
|
|
||||||
|
|
||||||
var Image = Map{
|
|
||||||
TypeJpeg: Jpeg,
|
|
||||||
TypeJpeg2000: Jpeg2000,
|
|
||||||
TypePng: Png,
|
|
||||||
TypeGif: Gif,
|
|
||||||
TypeWebp: Webp,
|
|
||||||
TypeCR2: CR2,
|
|
||||||
TypeTiff: Tiff,
|
|
||||||
TypeBmp: Bmp,
|
|
||||||
TypeJxr: Jxr,
|
|
||||||
TypePsd: Psd,
|
|
||||||
TypeIco: Ico,
|
|
||||||
TypeHeif: Heif,
|
|
||||||
TypeDwg: Dwg,
|
|
||||||
}
|
|
||||||
|
|
||||||
func Jpeg(buf []byte) bool {
|
|
||||||
return len(buf) > 2 &&
|
|
||||||
buf[0] == 0xFF &&
|
|
||||||
buf[1] == 0xD8 &&
|
|
||||||
buf[2] == 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
func Jpeg2000(buf []byte) bool {
|
|
||||||
return len(buf) > 12 &&
|
|
||||||
buf[0] == 0x0 &&
|
|
||||||
buf[1] == 0x0 &&
|
|
||||||
buf[2] == 0x0 &&
|
|
||||||
buf[3] == 0xC &&
|
|
||||||
buf[4] == 0x6A &&
|
|
||||||
buf[5] == 0x50 &&
|
|
||||||
buf[6] == 0x20 &&
|
|
||||||
buf[7] == 0x20 &&
|
|
||||||
buf[8] == 0xD &&
|
|
||||||
buf[9] == 0xA &&
|
|
||||||
buf[10] == 0x87 &&
|
|
||||||
buf[11] == 0xA &&
|
|
||||||
buf[12] == 0x0
|
|
||||||
}
|
|
||||||
|
|
||||||
func Png(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x89 && buf[1] == 0x50 &&
|
|
||||||
buf[2] == 0x4E && buf[3] == 0x47
|
|
||||||
}
|
|
||||||
|
|
||||||
func Gif(buf []byte) bool {
|
|
||||||
return len(buf) > 2 &&
|
|
||||||
buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46
|
|
||||||
}
|
|
||||||
|
|
||||||
func Webp(buf []byte) bool {
|
|
||||||
return len(buf) > 11 &&
|
|
||||||
buf[8] == 0x57 && buf[9] == 0x45 &&
|
|
||||||
buf[10] == 0x42 && buf[11] == 0x50
|
|
||||||
}
|
|
||||||
|
|
||||||
func CR2(buf []byte) bool {
|
|
||||||
return len(buf) > 10 &&
|
|
||||||
((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) || // Little Endian
|
|
||||||
(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) && // Big Endian
|
|
||||||
buf[8] == 0x43 && buf[9] == 0x52 && // CR2 magic word
|
|
||||||
buf[10] == 0x02 // CR2 major version
|
|
||||||
}
|
|
||||||
|
|
||||||
func Tiff(buf []byte) bool {
|
|
||||||
return len(buf) > 10 &&
|
|
||||||
((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) || // Little Endian
|
|
||||||
(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) && // Big Endian
|
|
||||||
!CR2(buf) // To avoid conflicts differentiate Tiff from CR2
|
|
||||||
}
|
|
||||||
|
|
||||||
func Bmp(buf []byte) bool {
|
|
||||||
return len(buf) > 1 &&
|
|
||||||
buf[0] == 0x42 &&
|
|
||||||
buf[1] == 0x4D
|
|
||||||
}
|
|
||||||
|
|
||||||
func Jxr(buf []byte) bool {
|
|
||||||
return len(buf) > 2 &&
|
|
||||||
buf[0] == 0x49 &&
|
|
||||||
buf[1] == 0x49 &&
|
|
||||||
buf[2] == 0xBC
|
|
||||||
}
|
|
||||||
|
|
||||||
func Psd(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x38 && buf[1] == 0x42 &&
|
|
||||||
buf[2] == 0x50 && buf[3] == 0x53
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ico(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x00 && buf[1] == 0x00 &&
|
|
||||||
buf[2] == 0x01 && buf[3] == 0x00
|
|
||||||
}
|
|
||||||
|
|
||||||
func Heif(buf []byte) bool {
|
|
||||||
if !isobmff.IsISOBMFF(buf) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
majorBrand, _, compatibleBrands := isobmff.GetFtyp(buf)
|
|
||||||
if majorBrand == "heic" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if majorBrand == "mif1" || majorBrand == "msf1" {
|
|
||||||
for _, compatibleBrand := range compatibleBrands {
|
|
||||||
if compatibleBrand == "heic" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func Dwg(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x41 && buf[1] == 0x43 &&
|
|
||||||
buf[2] == 0x31 && buf[3] == 0x30
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package isobmff
|
|
||||||
|
|
||||||
import "encoding/binary"
|
|
||||||
|
|
||||||
// IsISOBMFF checks whether the given buffer represents ISO Base Media File Format data
|
|
||||||
func IsISOBMFF(buf []byte) bool {
|
|
||||||
if len(buf) < 16 || string(buf[4:8]) != "ftyp" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if ftypLength := binary.BigEndian.Uint32(buf[0:4]); len(buf) < int(ftypLength) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFtyp returns the major brand, minor version and compatible brands of the ISO-BMFF data
|
|
||||||
func GetFtyp(buf []byte) (string, string, []string) {
|
|
||||||
if len(buf) < 17 {
|
|
||||||
return "", "", []string{""}
|
|
||||||
}
|
|
||||||
|
|
||||||
ftypLength := binary.BigEndian.Uint32(buf[0:4])
|
|
||||||
|
|
||||||
majorBrand := string(buf[8:12])
|
|
||||||
minorVersion := string(buf[12:16])
|
|
||||||
|
|
||||||
compatibleBrands := []string{}
|
|
||||||
for i := 16; i < int(ftypLength); i += 4 {
|
|
||||||
if len(buf) >= (i + 4) {
|
|
||||||
compatibleBrands = append(compatibleBrands, string(buf[i:i+4]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return majorBrand, minorVersion, compatibleBrands
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package matchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/h2non/filetype/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Internal shortcut to NewType
|
|
||||||
var newType = types.NewType
|
|
||||||
|
|
||||||
// Matcher function interface as type alias
|
|
||||||
type Matcher func([]byte) bool
|
|
||||||
|
|
||||||
// Type interface to store pairs of type with its matcher function
|
|
||||||
type Map map[types.Type]Matcher
|
|
||||||
|
|
||||||
// Type specific matcher function interface
|
|
||||||
type TypeMatcher func([]byte) types.Type
|
|
||||||
|
|
||||||
// Store registered file type matchers
|
|
||||||
var Matchers = make(map[types.Type]TypeMatcher)
|
|
||||||
var MatcherKeys []types.Type
|
|
||||||
|
|
||||||
// Create and register a new type matcher function
|
|
||||||
func NewMatcher(kind types.Type, fn Matcher) TypeMatcher {
|
|
||||||
matcher := func(buf []byte) types.Type {
|
|
||||||
if fn(buf) {
|
|
||||||
return kind
|
|
||||||
}
|
|
||||||
return types.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
Matchers[kind] = matcher
|
|
||||||
// prepend here so any user defined matchers get added first
|
|
||||||
MatcherKeys = append([]types.Type{kind}, MatcherKeys...)
|
|
||||||
return matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func register(matchers ...Map) {
|
|
||||||
MatcherKeys = MatcherKeys[:0]
|
|
||||||
for _, m := range matchers {
|
|
||||||
for kind, matcher := range m {
|
|
||||||
NewMatcher(kind, matcher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Arguments order is intentional
|
|
||||||
// Archive files will be checked last due to prepend above in func NewMatcher
|
|
||||||
register(Archive, Document, Font, Audio, Video, Image, Application)
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
package matchers
|
|
||||||
|
|
||||||
import "bytes"
|
|
||||||
|
|
||||||
var (
|
|
||||||
TypeMp4 = newType("mp4", "video/mp4")
|
|
||||||
TypeM4v = newType("m4v", "video/x-m4v")
|
|
||||||
TypeMkv = newType("mkv", "video/x-matroska")
|
|
||||||
TypeWebm = newType("webm", "video/webm")
|
|
||||||
TypeMov = newType("mov", "video/quicktime")
|
|
||||||
TypeAvi = newType("avi", "video/x-msvideo")
|
|
||||||
TypeWmv = newType("wmv", "video/x-ms-wmv")
|
|
||||||
TypeMpeg = newType("mpg", "video/mpeg")
|
|
||||||
TypeFlv = newType("flv", "video/x-flv")
|
|
||||||
Type3gp = newType("3gp", "video/3gpp")
|
|
||||||
)
|
|
||||||
|
|
||||||
var Video = Map{
|
|
||||||
TypeMp4: Mp4,
|
|
||||||
TypeM4v: M4v,
|
|
||||||
TypeMkv: Mkv,
|
|
||||||
TypeWebm: Webm,
|
|
||||||
TypeMov: Mov,
|
|
||||||
TypeAvi: Avi,
|
|
||||||
TypeWmv: Wmv,
|
|
||||||
TypeMpeg: Mpeg,
|
|
||||||
TypeFlv: Flv,
|
|
||||||
Type3gp: Match3gp,
|
|
||||||
}
|
|
||||||
|
|
||||||
func M4v(buf []byte) bool {
|
|
||||||
return len(buf) > 10 &&
|
|
||||||
buf[4] == 0x66 && buf[5] == 0x74 &&
|
|
||||||
buf[6] == 0x79 && buf[7] == 0x70 &&
|
|
||||||
buf[8] == 0x4D && buf[9] == 0x34 &&
|
|
||||||
buf[10] == 0x56
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mkv(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x1A && buf[1] == 0x45 &&
|
|
||||||
buf[2] == 0xDF && buf[3] == 0xA3 &&
|
|
||||||
containsMatroskaSignature(buf, []byte{'m', 'a', 't', 'r', 'o', 's', 'k', 'a'})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Webm(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x1A && buf[1] == 0x45 &&
|
|
||||||
buf[2] == 0xDF && buf[3] == 0xA3 &&
|
|
||||||
containsMatroskaSignature(buf, []byte{'w', 'e', 'b', 'm'})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mov(buf []byte) bool {
|
|
||||||
return len(buf) > 15 && ((buf[0] == 0x0 && buf[1] == 0x0 &&
|
|
||||||
buf[2] == 0x0 && buf[3] == 0x14 &&
|
|
||||||
buf[4] == 0x66 && buf[5] == 0x74 &&
|
|
||||||
buf[6] == 0x79 && buf[7] == 0x70) ||
|
|
||||||
(buf[4] == 0x6d && buf[5] == 0x6f && buf[6] == 0x6f && buf[7] == 0x76) ||
|
|
||||||
(buf[4] == 0x6d && buf[5] == 0x64 && buf[6] == 0x61 && buf[7] == 0x74) ||
|
|
||||||
(buf[12] == 0x6d && buf[13] == 0x64 && buf[14] == 0x61 && buf[15] == 0x74))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Avi(buf []byte) bool {
|
|
||||||
return len(buf) > 10 &&
|
|
||||||
buf[0] == 0x52 && buf[1] == 0x49 &&
|
|
||||||
buf[2] == 0x46 && buf[3] == 0x46 &&
|
|
||||||
buf[8] == 0x41 && buf[9] == 0x56 &&
|
|
||||||
buf[10] == 0x49
|
|
||||||
}
|
|
||||||
|
|
||||||
func Wmv(buf []byte) bool {
|
|
||||||
return len(buf) > 9 &&
|
|
||||||
buf[0] == 0x30 && buf[1] == 0x26 &&
|
|
||||||
buf[2] == 0xB2 && buf[3] == 0x75 &&
|
|
||||||
buf[4] == 0x8E && buf[5] == 0x66 &&
|
|
||||||
buf[6] == 0xCF && buf[7] == 0x11 &&
|
|
||||||
buf[8] == 0xA6 && buf[9] == 0xD9
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mpeg(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x0 && buf[1] == 0x0 &&
|
|
||||||
buf[2] == 0x1 && buf[3] >= 0xb0 &&
|
|
||||||
buf[3] <= 0xbf
|
|
||||||
}
|
|
||||||
|
|
||||||
func Flv(buf []byte) bool {
|
|
||||||
return len(buf) > 3 &&
|
|
||||||
buf[0] == 0x46 && buf[1] == 0x4C &&
|
|
||||||
buf[2] == 0x56 && buf[3] == 0x01
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mp4(buf []byte) bool {
|
|
||||||
return len(buf) > 11 &&
|
|
||||||
(buf[4] == 'f' && buf[5] == 't' && buf[6] == 'y' && buf[7] == 'p') &&
|
|
||||||
((buf[8] == 'a' && buf[9] == 'v' && buf[10] == 'c' && buf[11] == '1') ||
|
|
||||||
(buf[8] == 'd' && buf[9] == 'a' && buf[10] == 's' && buf[11] == 'h') ||
|
|
||||||
(buf[8] == 'i' && buf[9] == 's' && buf[10] == 'o' && buf[11] == '2') ||
|
|
||||||
(buf[8] == 'i' && buf[9] == 's' && buf[10] == 'o' && buf[11] == '3') ||
|
|
||||||
(buf[8] == 'i' && buf[9] == 's' && buf[10] == 'o' && buf[11] == '4') ||
|
|
||||||
(buf[8] == 'i' && buf[9] == 's' && buf[10] == 'o' && buf[11] == '5') ||
|
|
||||||
(buf[8] == 'i' && buf[9] == 's' && buf[10] == 'o' && buf[11] == '6') ||
|
|
||||||
(buf[8] == 'i' && buf[9] == 's' && buf[10] == 'o' && buf[11] == 'm') ||
|
|
||||||
(buf[8] == 'm' && buf[9] == 'm' && buf[10] == 'p' && buf[11] == '4') ||
|
|
||||||
(buf[8] == 'm' && buf[9] == 'p' && buf[10] == '4' && buf[11] == '1') ||
|
|
||||||
(buf[8] == 'm' && buf[9] == 'p' && buf[10] == '4' && buf[11] == '2') ||
|
|
||||||
(buf[8] == 'm' && buf[9] == 'p' && buf[10] == '4' && buf[11] == 'v') ||
|
|
||||||
(buf[8] == 'm' && buf[9] == 'p' && buf[10] == '7' && buf[11] == '1') ||
|
|
||||||
(buf[8] == 'M' && buf[9] == 'S' && buf[10] == 'N' && buf[11] == 'V') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'A' && buf[11] == 'S') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'S' && buf[11] == 'C') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'S' && buf[10] == 'D' && buf[11] == 'C') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'S' && buf[11] == 'H') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'S' && buf[11] == 'M') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'S' && buf[11] == 'P') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'S' && buf[11] == 'S') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'X' && buf[11] == 'C') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'X' && buf[11] == 'H') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'X' && buf[11] == 'M') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'X' && buf[11] == 'P') ||
|
|
||||||
(buf[8] == 'N' && buf[9] == 'D' && buf[10] == 'X' && buf[11] == 'S') ||
|
|
||||||
(buf[8] == 'F' && buf[9] == '4' && buf[10] == 'V' && buf[11] == ' ') ||
|
|
||||||
(buf[8] == 'F' && buf[9] == '4' && buf[10] == 'P' && buf[11] == ' '))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Match3gp(buf []byte) bool {
|
|
||||||
return len(buf) > 10 &&
|
|
||||||
buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 &&
|
|
||||||
buf[7] == 0x70 && buf[8] == 0x33 && buf[9] == 0x67 &&
|
|
||||||
buf[10] == 0x70
|
|
||||||
}
|
|
||||||
|
|
||||||
func containsMatroskaSignature(buf, subType []byte) bool {
|
|
||||||
limit := 4096
|
|
||||||
if len(buf) < limit {
|
|
||||||
limit = len(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
index := bytes.Index(buf[:limit], subType)
|
|
||||||
if index < 3 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf[index-3] == 0x42 && buf[index-2] == 0x82
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
// Unknown default type
|
|
||||||
var Unknown = NewType("unknown", "")
|
|
|
@ -1,14 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
// MIME stores the file MIME type values
|
|
||||||
type MIME struct {
|
|
||||||
Type string
|
|
||||||
Subtype string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new MIME type
|
|
||||||
func NewMIME(mime string) MIME {
|
|
||||||
kind, subtype := splitMime(mime)
|
|
||||||
return MIME{Type: kind, Subtype: subtype, Value: mime}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
func splitMime(s string) (string, string) {
|
|
||||||
x := strings.Split(s, "/")
|
|
||||||
if len(x) > 1 {
|
|
||||||
return x[0], x[1]
|
|
||||||
}
|
|
||||||
return x[0], ""
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
// Type represents a file MIME type and its extension
|
|
||||||
type Type struct {
|
|
||||||
MIME MIME
|
|
||||||
Extension string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewType creates a new Type
|
|
||||||
func NewType(ext, mime string) Type {
|
|
||||||
t := Type{
|
|
||||||
MIME: NewMIME(mime),
|
|
||||||
Extension: ext,
|
|
||||||
}
|
|
||||||
return Add(t)
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
// Types Support concurrent map writes
|
|
||||||
var Types sync.Map
|
|
||||||
|
|
||||||
// Add registers a new type in the package
|
|
||||||
func Add(t Type) Type {
|
|
||||||
Types.Store(t.Extension, t)
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves a Type by extension
|
|
||||||
func Get(ext string) Type {
|
|
||||||
if tmp, ok := Types.Load(ext); ok {
|
|
||||||
kind := tmp.(Type)
|
|
||||||
if kind.Extension != "" {
|
|
||||||
return kind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Unknown
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
package filetype
|
|
||||||
|
|
||||||
// Version exposes the current package version.
|
|
||||||
const Version = "1.1.3"
|
|
|
@ -1,9 +1,3 @@
|
||||||
# github.com/h2non/filetype v1.1.3
|
|
||||||
## explicit; go 1.13
|
|
||||||
github.com/h2non/filetype
|
|
||||||
github.com/h2non/filetype/matchers
|
|
||||||
github.com/h2non/filetype/matchers/isobmff
|
|
||||||
github.com/h2non/filetype/types
|
|
||||||
# github.com/inconshreveable/mousetrap v1.1.0
|
# github.com/inconshreveable/mousetrap v1.1.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/inconshreveable/mousetrap
|
github.com/inconshreveable/mousetrap
|
||||||
|
|
Loading…
Reference in New Issue