Add library for better mimetype detection
This commit is contained in:
Generated
+13
@@ -100,6 +100,18 @@
|
||||
pruneopts = "UT"
|
||||
revision = "b57537c92a6b4155c5219ce0d7d2a9c0cc74b1c4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:04904b7a6f24221e7999b554094e347885b4837beeba9933efadc93f4f5421ff"
|
||||
name = "github.com/gabriel-vasile/mimetype"
|
||||
packages = [
|
||||
".",
|
||||
"internal/json",
|
||||
"internal/matchers",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b9686d36d26efccccd34eff91a19f42a61bcdc95"
|
||||
version = "v0.3.17"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f59f34bb582fbc13885cd0338570bc0f21738fc711065527c36928213de62c1b"
|
||||
name = "github.com/getsentry/sentry-go"
|
||||
@@ -690,6 +702,7 @@
|
||||
"github.com/dgrijalva/jwt-go",
|
||||
"github.com/disintegration/imaging",
|
||||
"github.com/edwvee/exiffix",
|
||||
"github.com/gabriel-vasile/mimetype",
|
||||
"github.com/getsentry/sentry-go",
|
||||
"github.com/getsentry/sentry-go/http",
|
||||
"github.com/go-chi/chi",
|
||||
|
||||
@@ -29,3 +29,7 @@ required = [
|
||||
[[prune.project]]
|
||||
name = "github.com/cortezaproject/corteza-protobuf"
|
||||
unused-packages = false
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gabriel-vasile/mimetype"
|
||||
version = "0.3.17"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
testdata/* linguist-vendored
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
language: go
|
||||
go:
|
||||
- "1.9"
|
||||
- "1.10"
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get github.com/client9/misspell/cmd/misspell
|
||||
before_script:
|
||||
- go tool vet .
|
||||
script:
|
||||
- diff -u <(echo -n) <(gofmt -d ./)
|
||||
- go test -v
|
||||
- $GOPATH/bin/goveralls -service=travis-ci
|
||||
- misspell -locale US -error *.md *.go
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at vasile.gabriel@email.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
## Contribute
|
||||
Contributions to **mimetype** are welcome. If you find an issue and you consider
|
||||
contributing, you can use the [Github issues tracker](https://github.com/gabriel-vasile/mimetype/issues)
|
||||
in order to report it, or better yet, open a pull request.
|
||||
|
||||
Code contributions must respect these rules:
|
||||
- code must be test covered
|
||||
- code must be formatted using gofmt tool
|
||||
- exported names must be documented
|
||||
|
||||
**Important**: By submitting a pull request, you agree to allow the project
|
||||
owner to license your work under the same license as that used by the project.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018, 2019 Gabriel Vasile
|
||||
|
||||
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.
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<h1 align="center">
|
||||
mimetype
|
||||
</h1>
|
||||
|
||||
<h4 align="center">
|
||||
A package for detecting MIME types and extensions based on magic numbers
|
||||
</h4>
|
||||
<h6 align="center">
|
||||
No bindings, all written in pure go
|
||||
</h6>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/gabriel-vasile/mimetype">
|
||||
<img alt="Build Status" src="https://travis-ci.org/gabriel-vasile/mimetype.svg?branch=master">
|
||||
</a>
|
||||
<a href="https://godoc.org/github.com/gabriel-vasile/mimetype">
|
||||
<img alt="Documentation" src="https://godoc.org/github.com/gabriel-vasile/mimetype?status.svg">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/github.com/gabriel-vasile/mimetype">
|
||||
<img alt="Go report card" src="https://goreportcard.com/badge/github.com/gabriel-vasile/mimetype">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/gabriel-vasile/mimetype?branch=master">
|
||||
<img alt="Go report card" src="https://coveralls.io/repos/github/gabriel-vasile/mimetype/badge.svg?branch=master">
|
||||
</a>
|
||||
<a href="LICENSE">
|
||||
<img alt="License" src="https://img.shields.io/badge/License-MIT-green.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Install
|
||||
```bash
|
||||
go get github.com/gabriel-vasile/mimetype
|
||||
```
|
||||
|
||||
## Use
|
||||
The library exposes three functions you can use in order to detect a file type.
|
||||
See [Godoc](https://godoc.org/github.com/gabriel-vasile/mimetype) for full reference.
|
||||
```go
|
||||
func Detect(in []byte) (mime, extension string) {...}
|
||||
func DetectReader(r io.Reader) (mime, extension string, err error) {...}
|
||||
func DetectFile(file string) (mime, extension string, err error) {...}
|
||||
```
|
||||
When detecting from a `ReadSeeker` interface, such as `os.File`, make sure
|
||||
to reset the offset of the reader to the beginning if needed:
|
||||
```go
|
||||
_, err = file.Seek(0, io.SeekStart)
|
||||
```
|
||||
|
||||
## Supported MIME types
|
||||
See [supported mimes](supported_mimes.md) for the list of detected MIME types.
|
||||
If support is needed for a specific file format, please open an [issue](https://github.com/gabriel-vasile/mimetype/issues/new/choose).
|
||||
|
||||
## Structure
|
||||
**mimetype** uses an hierarchical structure to keep the matching functions.
|
||||
This reduces the number of calls needed for detecting the file type. The reason
|
||||
behind this choice is that there are file formats used as containers for other
|
||||
file formats. For example, Microsoft office files are just zip archives,
|
||||
containing specific metadata files.
|
||||
<div align="center">
|
||||
<img alt="structure" src="mimetype.gif" width="88%">
|
||||
</div>
|
||||
|
||||
## Contributing
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
+1
@@ -0,0 +1 @@
|
||||
module github.com/gabriel-vasile/mimetype
|
||||
+536
@@ -0,0 +1,536 @@
|
||||
// Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// JSON value parser state machine.
|
||||
// This package is almost entirely copied from the Go stdlib.
|
||||
// Changes made to it permit users of the package to tell
|
||||
// if some slice of bytes is a valid beginning of a json string.
|
||||
package json
|
||||
|
||||
import "fmt"
|
||||
|
||||
type (
|
||||
context int
|
||||
scanStatus int
|
||||
)
|
||||
|
||||
const (
|
||||
contextKey context = iota
|
||||
contextObj
|
||||
contextArr
|
||||
|
||||
scanContinue scanStatus = iota // uninteresting byte
|
||||
scanBeginLiteral // end implied by next result != scanContinue
|
||||
scanBeginObject // begin object
|
||||
scanObjectKey // just finished object key (string)
|
||||
scanObjectValue // just finished non-last object value
|
||||
scanEndObject // end object (implies scanObjectValue if possible)
|
||||
scanBeginArray // begin array
|
||||
scanArrayValue // just finished array value
|
||||
scanEndArray // end array (implies scanArrayValue if possible)
|
||||
scanSkipSpace // space byte; can skip; known to be last "continue" result
|
||||
scanEnd // top-level value ended *before* this byte; known to be first "stop" result
|
||||
scanError // hit an error, scanner.err.
|
||||
)
|
||||
|
||||
type (
|
||||
scanner struct {
|
||||
step func(*scanner, byte) scanStatus
|
||||
contexts []context
|
||||
endTop bool
|
||||
err error
|
||||
index int
|
||||
}
|
||||
)
|
||||
|
||||
// Scan returns the number of bytes scanned and if there was any error
|
||||
// in trying to reach the end of data
|
||||
func Scan(data []byte) (int, error) {
|
||||
s := &scanner{}
|
||||
_ = checkValid(data, s)
|
||||
return s.index, s.err
|
||||
}
|
||||
|
||||
// checkValid verifies that data is valid JSON-encoded data.
|
||||
// scan is passed in for use by checkValid to avoid an allocation.
|
||||
func checkValid(data []byte, scan *scanner) error {
|
||||
scan.reset()
|
||||
for _, c := range data {
|
||||
scan.index++
|
||||
if scan.step(scan, c) == scanError {
|
||||
return scan.err
|
||||
}
|
||||
}
|
||||
if scan.eof() == scanError {
|
||||
return scan.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isSpace(c byte) bool {
|
||||
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
|
||||
}
|
||||
|
||||
func (s *scanner) reset() {
|
||||
s.step = stateBeginValue
|
||||
s.contexts = s.contexts[0:0]
|
||||
s.err = nil
|
||||
}
|
||||
|
||||
// eof tells the scanner that the end of input has been reached.
|
||||
// It returns a scan status just as s.step does.
|
||||
func (s *scanner) eof() scanStatus {
|
||||
if s.err != nil {
|
||||
return scanError
|
||||
}
|
||||
if s.endTop {
|
||||
return scanEnd
|
||||
}
|
||||
s.step(s, ' ')
|
||||
if s.endTop {
|
||||
return scanEnd
|
||||
}
|
||||
if s.err == nil {
|
||||
s.err = fmt.Errorf("unexpected end of JSON input")
|
||||
}
|
||||
return scanError
|
||||
}
|
||||
|
||||
// pushContext pushes a new parse state p onto the parse stack.
|
||||
func (s *scanner) pushParseState(p context) {
|
||||
s.contexts = append(s.contexts, p)
|
||||
}
|
||||
|
||||
// popParseState pops a parse state (already obtained) off the stack
|
||||
// and updates s.step accordingly.
|
||||
func (s *scanner) popParseState() {
|
||||
n := len(s.contexts) - 1
|
||||
s.contexts = s.contexts[0:n]
|
||||
if n == 0 {
|
||||
s.step = stateEndTop
|
||||
s.endTop = true
|
||||
} else {
|
||||
s.step = stateEndValue
|
||||
}
|
||||
}
|
||||
|
||||
// stateBeginValueOrEmpty is the state after reading `[`.
|
||||
func stateBeginValueOrEmpty(s *scanner, c byte) scanStatus {
|
||||
if c <= ' ' && isSpace(c) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == ']' {
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
return stateBeginValue(s, c)
|
||||
}
|
||||
|
||||
// stateBeginValue is the state at the beginning of the input.
|
||||
func stateBeginValue(s *scanner, c byte) scanStatus {
|
||||
if c <= ' ' && isSpace(c) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
switch c {
|
||||
case '{':
|
||||
s.step = stateBeginStringOrEmpty
|
||||
s.pushParseState(contextKey)
|
||||
return scanBeginObject
|
||||
case '[':
|
||||
s.step = stateBeginValueOrEmpty
|
||||
s.pushParseState(contextArr)
|
||||
return scanBeginArray
|
||||
case '"':
|
||||
s.step = stateInString
|
||||
return scanBeginLiteral
|
||||
case '-':
|
||||
s.step = stateNeg
|
||||
return scanBeginLiteral
|
||||
case '0': // beginning of 0.123
|
||||
s.step = state0
|
||||
return scanBeginLiteral
|
||||
case 't': // beginning of true
|
||||
s.step = stateT
|
||||
return scanBeginLiteral
|
||||
case 'f': // beginning of false
|
||||
s.step = stateF
|
||||
return scanBeginLiteral
|
||||
case 'n': // beginning of null
|
||||
s.step = stateN
|
||||
return scanBeginLiteral
|
||||
}
|
||||
if '1' <= c && c <= '9' { // beginning of 1234.5
|
||||
s.step = state1
|
||||
return scanBeginLiteral
|
||||
}
|
||||
return s.error(c, "looking for beginning of value")
|
||||
}
|
||||
|
||||
// stateBeginStringOrEmpty is the state after reading `{`.
|
||||
func stateBeginStringOrEmpty(s *scanner, c byte) scanStatus {
|
||||
if c <= ' ' && isSpace(c) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == '}' {
|
||||
n := len(s.contexts)
|
||||
s.contexts[n-1] = contextObj
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
return stateBeginString(s, c)
|
||||
}
|
||||
|
||||
// stateBeginString is the state after reading `{"key": value,`.
|
||||
func stateBeginString(s *scanner, c byte) scanStatus {
|
||||
if c <= ' ' && isSpace(c) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == '"' {
|
||||
s.step = stateInString
|
||||
return scanBeginLiteral
|
||||
}
|
||||
return s.error(c, "looking for beginning of object key string")
|
||||
}
|
||||
|
||||
// stateEndValue is the state after completing a value,
|
||||
// such as after reading `{}` or `true` or `["x"`.
|
||||
func stateEndValue(s *scanner, c byte) scanStatus {
|
||||
n := len(s.contexts)
|
||||
if n == 0 {
|
||||
// Completed top-level before the current byte.
|
||||
s.step = stateEndTop
|
||||
s.endTop = true
|
||||
return stateEndTop(s, c)
|
||||
}
|
||||
if c <= ' ' && isSpace(c) {
|
||||
s.step = stateEndValue
|
||||
return scanSkipSpace
|
||||
}
|
||||
ps := s.contexts[n-1]
|
||||
switch ps {
|
||||
case contextKey:
|
||||
if c == ':' {
|
||||
s.contexts[n-1] = contextObj
|
||||
s.step = stateBeginValue
|
||||
return scanObjectKey
|
||||
}
|
||||
return s.error(c, "after object key")
|
||||
case contextObj:
|
||||
if c == ',' {
|
||||
s.contexts[n-1] = contextKey
|
||||
s.step = stateBeginString
|
||||
return scanObjectValue
|
||||
}
|
||||
if c == '}' {
|
||||
s.popParseState()
|
||||
return scanEndObject
|
||||
}
|
||||
return s.error(c, "after object key:value pair")
|
||||
case contextArr:
|
||||
if c == ',' {
|
||||
s.step = stateBeginValue
|
||||
return scanArrayValue
|
||||
}
|
||||
if c == ']' {
|
||||
s.popParseState()
|
||||
return scanEndArray
|
||||
}
|
||||
return s.error(c, "after array element")
|
||||
}
|
||||
return s.error(c, "")
|
||||
}
|
||||
|
||||
// stateEndTop is the state after finishing the top-level value,
|
||||
// such as after reading `{}` or `[1,2,3]`.
|
||||
// Only space characters should be seen now.
|
||||
func stateEndTop(s *scanner, c byte) scanStatus {
|
||||
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
|
||||
// Complain about non-space byte on next call.
|
||||
s.error(c, "after top-level value")
|
||||
}
|
||||
return scanEnd
|
||||
}
|
||||
|
||||
// stateInString is the state after reading `"`.
|
||||
func stateInString(s *scanner, c byte) scanStatus {
|
||||
if c == '"' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
if c == '\\' {
|
||||
s.step = stateInStringEsc
|
||||
return scanContinue
|
||||
}
|
||||
if c < 0x20 {
|
||||
return s.error(c, "in string literal")
|
||||
}
|
||||
return scanContinue
|
||||
}
|
||||
|
||||
// stateInStringEsc is the state after reading `"\` during a quoted string.
|
||||
func stateInStringEsc(s *scanner, c byte) scanStatus {
|
||||
switch c {
|
||||
case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
|
||||
s.step = stateInString
|
||||
return scanContinue
|
||||
case 'u':
|
||||
s.step = stateInStringEscU
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in string escape code")
|
||||
}
|
||||
|
||||
// stateInStringEscU is the state after reading `"\u` during a quoted string.
|
||||
func stateInStringEscU(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU1
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
|
||||
func stateInStringEscU1(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU12
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
|
||||
func stateInStringEscU12(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU123
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
|
||||
func stateInStringEscU123(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInString
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateNeg is the state after reading `-` during a number.
|
||||
func stateNeg(s *scanner, c byte) scanStatus {
|
||||
if c == '0' {
|
||||
s.step = state0
|
||||
return scanContinue
|
||||
}
|
||||
if '1' <= c && c <= '9' {
|
||||
s.step = state1
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in numeric literal")
|
||||
}
|
||||
|
||||
// state1 is the state after reading a non-zero integer during a number,
|
||||
// such as after reading `1` or `100` but not `0`.
|
||||
func state1(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = state1
|
||||
return scanContinue
|
||||
}
|
||||
return state0(s, c)
|
||||
}
|
||||
|
||||
// state0 is the state after reading `0` during a number.
|
||||
func state0(s *scanner, c byte) scanStatus {
|
||||
if c == '.' {
|
||||
s.step = stateDot
|
||||
return scanContinue
|
||||
}
|
||||
if c == 'e' || c == 'E' {
|
||||
s.step = stateE
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateDot is the state after reading the integer and decimal point in a number,
|
||||
// such as after reading `1.`.
|
||||
func stateDot(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = stateDot0
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "after decimal point in numeric literal")
|
||||
}
|
||||
|
||||
// stateDot0 is the state after reading the integer, decimal point, and subsequent
|
||||
// digits of a number, such as after reading `3.14`.
|
||||
func stateDot0(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' {
|
||||
return scanContinue
|
||||
}
|
||||
if c == 'e' || c == 'E' {
|
||||
s.step = stateE
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateE is the state after reading the mantissa and e in a number,
|
||||
// such as after reading `314e` or `0.314e`.
|
||||
func stateE(s *scanner, c byte) scanStatus {
|
||||
if c == '+' || c == '-' {
|
||||
s.step = stateESign
|
||||
return scanContinue
|
||||
}
|
||||
return stateESign(s, c)
|
||||
}
|
||||
|
||||
// stateESign is the state after reading the mantissa, e, and sign in a number,
|
||||
// such as after reading `314e-` or `0.314e+`.
|
||||
func stateESign(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = stateE0
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in exponent of numeric literal")
|
||||
}
|
||||
|
||||
// stateE0 is the state after reading the mantissa, e, optional sign,
|
||||
// and at least one digit of the exponent in a number,
|
||||
// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
|
||||
func stateE0(s *scanner, c byte) scanStatus {
|
||||
if '0' <= c && c <= '9' {
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateT is the state after reading `t`.
|
||||
func stateT(s *scanner, c byte) scanStatus {
|
||||
if c == 'r' {
|
||||
s.step = stateTr
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'r')")
|
||||
}
|
||||
|
||||
// stateTr is the state after reading `tr`.
|
||||
func stateTr(s *scanner, c byte) scanStatus {
|
||||
if c == 'u' {
|
||||
s.step = stateTru
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'u')")
|
||||
}
|
||||
|
||||
// stateTru is the state after reading `tru`.
|
||||
func stateTru(s *scanner, c byte) scanStatus {
|
||||
if c == 'e' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'e')")
|
||||
}
|
||||
|
||||
// stateF is the state after reading `f`.
|
||||
func stateF(s *scanner, c byte) scanStatus {
|
||||
if c == 'a' {
|
||||
s.step = stateFa
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'a')")
|
||||
}
|
||||
|
||||
// stateFa is the state after reading `fa`.
|
||||
func stateFa(s *scanner, c byte) scanStatus {
|
||||
if c == 'l' {
|
||||
s.step = stateFal
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateFal is the state after reading `fal`.
|
||||
func stateFal(s *scanner, c byte) scanStatus {
|
||||
if c == 's' {
|
||||
s.step = stateFals
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 's')")
|
||||
}
|
||||
|
||||
// stateFals is the state after reading `fals`.
|
||||
func stateFals(s *scanner, c byte) scanStatus {
|
||||
if c == 'e' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'e')")
|
||||
}
|
||||
|
||||
// stateN is the state after reading `n`.
|
||||
func stateN(s *scanner, c byte) scanStatus {
|
||||
if c == 'u' {
|
||||
s.step = stateNu
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'u')")
|
||||
}
|
||||
|
||||
// stateNu is the state after reading `nu`.
|
||||
func stateNu(s *scanner, c byte) scanStatus {
|
||||
if c == 'l' {
|
||||
s.step = stateNul
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateNul is the state after reading `nul`.
|
||||
func stateNul(s *scanner, c byte) scanStatus {
|
||||
if c == 'l' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateError is the state after reaching a syntax error,
|
||||
// such as after reading `[1}` or `5.1.2`.
|
||||
func stateError(s *scanner, c byte) scanStatus {
|
||||
return scanError
|
||||
}
|
||||
|
||||
// error records an error and switches to the error state.
|
||||
func (s *scanner) error(c byte, context string) scanStatus {
|
||||
s.step = stateError
|
||||
s.err = fmt.Errorf("invalid character <<%c>> %s", c, context)
|
||||
return scanError
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package matchers
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Zip matches a zip archive.
|
||||
func Zip(in []byte) bool {
|
||||
return len(in) > 3 &&
|
||||
in[0] == 0x50 && in[1] == 0x4B &&
|
||||
(in[2] == 0x3 || in[2] == 0x5 || in[2] == 0x7) &&
|
||||
(in[3] == 0x4 || in[3] == 0x6 || in[3] == 0x8)
|
||||
}
|
||||
|
||||
// SevenZ matches a 7z archive.
|
||||
func SevenZ(in []byte) bool {
|
||||
return len(in) > 6 &&
|
||||
bytes.Equal(in[:6], []byte{0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C})
|
||||
}
|
||||
|
||||
// Epub matches an EPUB file.
|
||||
func Epub(in []byte) bool {
|
||||
return len(in) > 58 && bytes.Equal(in[30:58], []byte("mimetypeapplication/epub+zip"))
|
||||
}
|
||||
|
||||
// Jar matches a Java archive file.
|
||||
func Jar(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("META-INF/MANIFEST.MF"))
|
||||
}
|
||||
|
||||
// Gzip matched gzip files based on http://www.zlib.org/rfc-gzip.html#header-trailer.
|
||||
func Gzip(in []byte) bool {
|
||||
return len(in) > 2 && bytes.Equal(in[:2], []byte{0x1f, 0x8b})
|
||||
}
|
||||
|
||||
// Crx matches a Chrome extension file: a zip archive prepended by "Cr24".
|
||||
func Crx(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("Cr24"))
|
||||
}
|
||||
|
||||
// Tar matches a (t)ape (ar)chive file.
|
||||
func Tar(in []byte) bool {
|
||||
return len(in) > 262 && bytes.Equal(in[257:262], []byte("ustar"))
|
||||
}
|
||||
|
||||
// Fits matches an Flexible Image Transport System file.
|
||||
func Fits(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x53, 0x49, 0x4D, 0x50, 0x4C, 0x45, 0x20,
|
||||
0x20, 0x3D, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54})
|
||||
}
|
||||
|
||||
// Xar matches an eXtensible ARchive format file.
|
||||
func Xar(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x78, 0x61, 0x72, 0x21})
|
||||
}
|
||||
|
||||
// Bz2 matches a bzip2 file.
|
||||
func Bz2(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x42, 0x5A, 0x68})
|
||||
}
|
||||
|
||||
// Ar matches an ar (Unix) archive file.
|
||||
func Ar(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E})
|
||||
}
|
||||
|
||||
// Deb matches a Debian package file
|
||||
func Deb(in []byte) bool {
|
||||
return len(in) > 8 && bytes.HasPrefix(in[8:], []byte{0x64, 0x65, 0x62, 0x69,
|
||||
0x61, 0x6E, 0x2D, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79})
|
||||
}
|
||||
|
||||
// Rar matches a RAR archive file
|
||||
func Rar(in []byte) bool {
|
||||
if !bytes.HasPrefix(in, []byte{0x52, 0x61, 0x72, 0x21, 0x1A, 0x07}) {
|
||||
return false
|
||||
}
|
||||
return len(in) > 8 && (bytes.Equal(in[6:8], []byte{0x01, 0x00}) || in[6] == 0x00)
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// Mp3 matches an mp3 file.
|
||||
func Mp3(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("\x49\x44\x33"))
|
||||
}
|
||||
|
||||
// Flac matches a Free Lossless Audio Codec file.
|
||||
func Flac(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("\x66\x4C\x61\x43\x00\x00\x00\x22"))
|
||||
}
|
||||
|
||||
// Midi matches a Musical Instrument Digital Interface file.
|
||||
func Midi(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("\x4D\x54\x68\x64"))
|
||||
}
|
||||
|
||||
// Ape matches a Monkey's Audio file.
|
||||
func Ape(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("\x4D\x41\x43\x20\x96\x0F\x00\x00\x34\x00\x00\x00\x18\x00\x00\x00\x90\xE3"))
|
||||
}
|
||||
|
||||
// MusePack matches a Musepack file.
|
||||
func MusePack(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("MPCK"))
|
||||
}
|
||||
|
||||
// Wav matches a Waveform Audio File Format file.
|
||||
func Wav(in []byte) bool {
|
||||
return len(in) > 12 &&
|
||||
bytes.Equal(in[:4], []byte("\x52\x49\x46\x46")) &&
|
||||
bytes.Equal(in[8:12], []byte("\x57\x41\x56\x45"))
|
||||
}
|
||||
|
||||
// Aiff matches Audio Interchange File Format file.
|
||||
func Aiff(in []byte) bool {
|
||||
return len(in) > 12 &&
|
||||
bytes.Equal(in[:4], []byte("\x46\x4F\x52\x4D")) &&
|
||||
bytes.Equal(in[8:12], []byte("\x41\x49\x46\x46"))
|
||||
}
|
||||
|
||||
// Ogg matches an Ogg file.
|
||||
func Ogg(in []byte) bool {
|
||||
return len(in) > 5 && bytes.Equal(in[:5], []byte("\x4F\x67\x67\x53\x00"))
|
||||
}
|
||||
|
||||
// Au matches a Sun Microsystems au file.
|
||||
func Au(in []byte) bool {
|
||||
return len(in) > 4 && bytes.Equal(in[:4], []byte("\x2E\x73\x6E\x64"))
|
||||
}
|
||||
|
||||
// Amr matches an Adaptive Multi-Rate file.
|
||||
func Amr(in []byte) bool {
|
||||
return len(in) > 5 && bytes.Equal(in[:5], []byte("\x23\x21\x41\x4D\x52"))
|
||||
}
|
||||
|
||||
// Aac matches an Advanced Audio Coding file.
|
||||
func Aac(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0xFF, 0xF1}) || bytes.HasPrefix(in, []byte{0xFF, 0xF9})
|
||||
}
|
||||
|
||||
// Voc matches a Creative Voice file.
|
||||
func Voc(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("Creative Voice File"))
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// Class matches an java class file.
|
||||
func Class(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0xCA, 0xFE, 0xBA, 0xBE})
|
||||
}
|
||||
|
||||
// Swf matches an Adobe Flash swf file.
|
||||
func Swf(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("CWS")) ||
|
||||
bytes.HasPrefix(in, []byte("FWS")) ||
|
||||
bytes.HasPrefix(in, []byte("ZWS"))
|
||||
}
|
||||
|
||||
// Wasm matches a web assembly File Format file.
|
||||
func Wasm(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x00, 0x61, 0x73, 0x6D})
|
||||
}
|
||||
|
||||
// Dbf matches a dBase file.
|
||||
// https://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm
|
||||
func Dbf(in []byte) bool {
|
||||
if len(in) < 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 3rd and 4th bytes contain the last update month and day of month
|
||||
if !(0 < in[2] && in[2] < 13 && 0 < in[3] && in[3] < 32) {
|
||||
return false
|
||||
}
|
||||
|
||||
// dbf type is dictated by the first byte
|
||||
dbfTypes := []byte{
|
||||
0x02, 0x03, 0x04, 0x05, 0x30, 0x31, 0x32, 0x42, 0x62, 0x7B, 0x82,
|
||||
0x83, 0x87, 0x8A, 0x8B, 0x8E, 0xB3, 0xCB, 0xE5, 0xF5, 0xF4, 0xFB,
|
||||
}
|
||||
for _, b := range dbfTypes {
|
||||
if in[0] == b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Exe matches a Windows/DOS executable file.
|
||||
func Exe(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x4D, 0x5A})
|
||||
}
|
||||
|
||||
// Elf matches an Executable and Linkable Format file.
|
||||
func Elf(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x7F, 0x45, 0x4C, 0x46})
|
||||
}
|
||||
|
||||
// ElfObj matches an object file.
|
||||
func ElfObj(in []byte) bool {
|
||||
return len(in) > 17 && ((in[16] == 0x01 && in[17] == 0x00) ||
|
||||
(in[16] == 0x00 && in[17] == 0x01))
|
||||
}
|
||||
|
||||
// ElfExe matches an executable file.
|
||||
func ElfExe(in []byte) bool {
|
||||
return len(in) > 17 && ((in[16] == 0x02 && in[17] == 0x00) ||
|
||||
(in[16] == 0x00 && in[17] == 0x02))
|
||||
}
|
||||
|
||||
// ElfLib matches a shared library file.
|
||||
func ElfLib(in []byte) bool {
|
||||
return len(in) > 17 && ((in[16] == 0x03 && in[17] == 0x00) ||
|
||||
(in[16] == 0x00 && in[17] == 0x03))
|
||||
}
|
||||
|
||||
// ElfDump matches a core dump file.
|
||||
func ElfDump(in []byte) bool {
|
||||
return len(in) > 17 && ((in[16] == 0x04 && in[17] == 0x00) ||
|
||||
(in[16] == 0x00 && in[17] == 0x04))
|
||||
}
|
||||
|
||||
// Dcm matches a DICOM medical format file.
|
||||
func Dcm(in []byte) bool {
|
||||
return len(in) > 131 &&
|
||||
bytes.Equal(in[128:132], []byte{0x44, 0x49, 0x43, 0x4D})
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package matchers
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Sqlite matches an SQLite database file.
|
||||
func Sqlite(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{
|
||||
0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00,
|
||||
})
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package matchers
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Pdf matches a Portable Document Format file.
|
||||
func Pdf(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x25, 0x50, 0x44, 0x46})
|
||||
}
|
||||
|
||||
// DjVu matches a DjVu file
|
||||
func DjVu(in []byte) bool {
|
||||
if !bytes.HasPrefix(in, []byte{0x41, 0x54, 0x26, 0x54, 0x46, 0x4F, 0x52, 0x4D}) {
|
||||
return false
|
||||
}
|
||||
if len(in) < 15 {
|
||||
return false
|
||||
}
|
||||
return bytes.HasPrefix(in[12:], []byte("DJVM")) ||
|
||||
bytes.HasPrefix(in[12:], []byte("DJVU")) ||
|
||||
bytes.HasPrefix(in[12:], []byte("DJVI")) ||
|
||||
bytes.HasPrefix(in[12:], []byte("THUM"))
|
||||
}
|
||||
|
||||
// Mobi matches a Mobi file
|
||||
func Mobi(in []byte) bool {
|
||||
if len(in) < 68 {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(in[60:68], []byte("BOOKMOBI"))
|
||||
}
|
||||
|
||||
// Lit matches a Microsoft Lit file
|
||||
func Lit(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("ITOLITLS"))
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package matchers
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Woff matches a Web Open Font Format file.
|
||||
func Woff(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("wOFF"))
|
||||
}
|
||||
|
||||
// Woff2 matches a Web Open Font Format version 2 file.
|
||||
func Woff2(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("wOF2"))
|
||||
}
|
||||
|
||||
// Otf matches an OpenType font file.
|
||||
func Otf(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x4F, 0x54, 0x54, 0x4F, 0x00})
|
||||
}
|
||||
|
||||
// Eot matches an Embedded OpenType font file.
|
||||
func Eot(in []byte) bool {
|
||||
return len(in) > 35 &&
|
||||
bytes.Equal(in[34:36], []byte{0x4C, 0x50}) &&
|
||||
(bytes.Equal(in[8:11], []byte{0x02, 0x00, 0x01}) ||
|
||||
bytes.Equal(in[8:11], []byte{0x01, 0x00, 0x00}) ||
|
||||
bytes.Equal(in[8:11], []byte{0x02, 0x00, 0x02}))
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// Shp matches a shape format file.
|
||||
// https://www.esri.com/library/whitepapers/pdfs/shapefile.pdf
|
||||
func Shp(in []byte) bool {
|
||||
if len(in) < 112 {
|
||||
return false
|
||||
}
|
||||
shapeTypes := []int{
|
||||
0, // Null shape
|
||||
1, // Point
|
||||
3, // Polyline
|
||||
5, // Polygon
|
||||
8, // MultiPoint
|
||||
11, // PointZ
|
||||
13, // PolylineZ
|
||||
15, // PolygonZ
|
||||
18, // MultiPointZ
|
||||
21, // PointM
|
||||
23, // PolylineM
|
||||
25, // PolygonM
|
||||
28, // MultiPointM
|
||||
31, // MultiPatch
|
||||
}
|
||||
|
||||
for _, st := range shapeTypes {
|
||||
if st == int(binary.LittleEndian.Uint32(in[108:112])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Shx matches a shape index format file.
|
||||
// https://www.esri.com/library/whitepapers/pdfs/shapefile.pdf
|
||||
func Shx(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x00, 0x00, 0x27, 0x0A})
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package matchers
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Png matches a Portable Network Graphics file.
|
||||
func Png(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A})
|
||||
}
|
||||
|
||||
// Jpg matches a Joint Photographic Experts Group file.
|
||||
func Jpg(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0xFF, 0xD8, 0xFF})
|
||||
}
|
||||
|
||||
// Gif matches a Graphics Interchange Format file.
|
||||
func Gif(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("GIF87a")) ||
|
||||
bytes.HasPrefix(in, []byte("GIF89a"))
|
||||
}
|
||||
|
||||
// Webp matches a WebP file.
|
||||
func Webp(in []byte) bool {
|
||||
return len(in) > 12 &&
|
||||
bytes.Equal(in[0:4], []byte{0x52, 0x49, 0x46, 0x46}) &&
|
||||
bytes.Equal(in[8:12], []byte{0x57, 0x45, 0x42, 0x50})
|
||||
}
|
||||
|
||||
// Bmp matches a bitmap image file.
|
||||
func Bmp(in []byte) bool {
|
||||
return len(in) > 1 && in[0] == 0x42 && in[1] == 0x4D
|
||||
}
|
||||
|
||||
// Ps matches a PostScript file.
|
||||
func Ps(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("%!PS-Adobe-"))
|
||||
}
|
||||
|
||||
// Psd matches a Photoshop Document file.
|
||||
func Psd(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("8BPS"))
|
||||
}
|
||||
|
||||
// Ico matches an ICO file.
|
||||
func Ico(in []byte) bool {
|
||||
return len(in) > 3 &&
|
||||
in[0] == 0x00 && in[1] == 0x00 &&
|
||||
in[2] == 0x01 && in[3] == 0x00
|
||||
}
|
||||
|
||||
// Tiff matches a Tagged Image File Format file.
|
||||
func Tiff(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x49, 0x49, 0x2A, 0x00}) ||
|
||||
bytes.HasPrefix(in, []byte{0x4D, 0x4D, 0x00, 0x2A})
|
||||
}
|
||||
|
||||
// Bpg matches a Better Portable Graphics file.
|
||||
func Bpg(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x42, 0x50, 0x47, 0xFB})
|
||||
}
|
||||
|
||||
// Dwg matches a CAD drawing file.
|
||||
func Dwg(in []byte) bool {
|
||||
if len(in) < 6 || in[0] != 0x41 || in[1] != 0x43 {
|
||||
return false
|
||||
}
|
||||
dwgVersions := [][]byte{
|
||||
{0x31, 0x2E, 0x34, 0x30},
|
||||
{0x31, 0x2E, 0x35, 0x30},
|
||||
{0x32, 0x2E, 0x31, 0x30},
|
||||
{0x31, 0x30, 0x30, 0x32},
|
||||
{0x31, 0x30, 0x30, 0x33},
|
||||
{0x31, 0x30, 0x30, 0x34},
|
||||
{0x31, 0x30, 0x30, 0x36},
|
||||
{0x31, 0x30, 0x30, 0x39},
|
||||
{0x31, 0x30, 0x31, 0x32},
|
||||
{0x31, 0x30, 0x31, 0x34},
|
||||
{0x31, 0x30, 0x31, 0x35},
|
||||
{0x31, 0x30, 0x31, 0x38},
|
||||
{0x31, 0x30, 0x32, 0x31},
|
||||
{0x31, 0x30, 0x32, 0x34},
|
||||
{0x31, 0x30, 0x33, 0x32},
|
||||
}
|
||||
|
||||
for _, d := range dwgVersions {
|
||||
if bytes.Equal(in[2:6], d) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// Package matchers holds the matching functions used to find mime types.
|
||||
package matchers
|
||||
|
||||
// ReadLimit is the maximum number of bytes read
|
||||
// from the input when detecting a reader.
|
||||
const ReadLimit = 2048
|
||||
|
||||
// True is a dummy matching function used to match any input.
|
||||
func True([]byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// False is a dummy matching function used to never match input.
|
||||
func False([]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// trimLWS trims whitespace from beginning of the input.
|
||||
func trimLWS(in []byte) []byte {
|
||||
firstNonWS := 0
|
||||
for ; firstNonWS < len(in) && isWS(in[firstNonWS]); firstNonWS++ {
|
||||
}
|
||||
|
||||
return in[firstNonWS:]
|
||||
}
|
||||
|
||||
// trimRWS trims whitespace from the end of the input.
|
||||
func trimRWS(in []byte) []byte {
|
||||
lastNonWS := len(in) - 1
|
||||
for ; lastNonWS > 0 && isWS(in[lastNonWS]); lastNonWS-- {
|
||||
}
|
||||
|
||||
return in[:lastNonWS+1]
|
||||
}
|
||||
|
||||
func firstLine(in []byte) []byte {
|
||||
lineEnd := 0
|
||||
for ; lineEnd < len(in) && in[lineEnd] != '\n'; lineEnd++ {
|
||||
}
|
||||
|
||||
return in[:lineEnd]
|
||||
}
|
||||
|
||||
func isWS(b byte) bool {
|
||||
return b == '\t' || b == '\n' || b == '\x0c' || b == '\r' || b == ' '
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Xlsx matches a Microsoft Excel 2007 file.
|
||||
func Xlsx(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("xl/"))
|
||||
}
|
||||
|
||||
// Docx matches a Microsoft Office 2007 file.
|
||||
func Docx(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("word/"))
|
||||
}
|
||||
|
||||
// Pptx matches a Microsoft PowerPoint 2007 file.
|
||||
func Pptx(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("ppt/"))
|
||||
}
|
||||
|
||||
// Doc matches a Microsoft Office 97-2003 file.
|
||||
func Doc(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1})
|
||||
}
|
||||
|
||||
// Ppt matches a Microsoft PowerPoint 97-2003 file.
|
||||
func Ppt(in []byte) bool {
|
||||
if len(in) < 520 {
|
||||
return false
|
||||
}
|
||||
|
||||
if fmt.Sprintf("%X", in[:8]) == "D0CF11E0A1B11AE1" {
|
||||
offset512 := fmt.Sprintf("%X", in[512:516])
|
||||
if offset512 == "A0461DF0" || offset512 == "006E1EF0" || offset512 == "0F00E803" {
|
||||
return true
|
||||
}
|
||||
if offset512 == "FDFFFFFF" && fmt.Sprintf("%x", in[518:520]) == "0000" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Xls matches a Microsoft Excel 97-2003 file.
|
||||
func Xls(in []byte) bool {
|
||||
if len(in) < 520 {
|
||||
return false
|
||||
}
|
||||
|
||||
if fmt.Sprintf("%X", in[:8]) == "D0CF11E0A1B11AE1" {
|
||||
offset512 := fmt.Sprintf("%X", in[512:520])
|
||||
subheaders := []string{
|
||||
"0908100000060500",
|
||||
"FDFFFFFF10",
|
||||
"FDFFFFFF1F",
|
||||
"FDFFFFFF22",
|
||||
"FDFFFFFF23",
|
||||
"FDFFFFFF28",
|
||||
"FDFFFFFF29",
|
||||
}
|
||||
for _, h := range subheaders {
|
||||
if strings.HasPrefix(offset512, h) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package matchers
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Odt matches an OpenDocument Text file.
|
||||
func Odt(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.text"))
|
||||
}
|
||||
|
||||
// Ott matches an OpenDocument Text Template file.
|
||||
func Ott(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.text-template"))
|
||||
}
|
||||
|
||||
// Ods matches an OpenDocument Spreadsheet file.
|
||||
func Ods(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.spreadsheet"))
|
||||
}
|
||||
|
||||
// Ots matches an OpenDocument Spreadsheet Template file.
|
||||
func Ots(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.spreadsheet-template"))
|
||||
}
|
||||
|
||||
// Odp matches an OpenDocument Presentation file.
|
||||
func Odp(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.presentation"))
|
||||
}
|
||||
|
||||
// Otp matches an OpenDocument Presentation Template file.
|
||||
func Otp(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.presentation-template"))
|
||||
}
|
||||
|
||||
// Odg matches an OpenDocument Drawing file.
|
||||
func Odg(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.graphics"))
|
||||
}
|
||||
|
||||
// Otg matches an OpenDocument Drawing Template file.
|
||||
func Otg(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.graphics-template"))
|
||||
}
|
||||
|
||||
// Odf matches an OpenDocument Formula file.
|
||||
func Odf(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("mimetypeapplication/vnd.oasis.opendocument.formula"))
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type (
|
||||
markupSig []byte
|
||||
ciSig []byte // case insensitive signature
|
||||
shebangSig []byte // matches !# followed by the signature
|
||||
ftypSig []byte // matches audio/video files. www.ftyps.com
|
||||
xmlSig struct {
|
||||
// the local name of the root tag
|
||||
localName []byte
|
||||
// the namespace of the XML document
|
||||
xmlns []byte
|
||||
}
|
||||
sig interface {
|
||||
detect([]byte) bool
|
||||
}
|
||||
)
|
||||
|
||||
func newXmlSig(localName, xmlns string) xmlSig {
|
||||
ret := xmlSig{xmlns: []byte(xmlns)}
|
||||
if localName != "" {
|
||||
ret.localName = []byte(fmt.Sprintf("<%s", localName))
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Implement sig interface.
|
||||
func (hSig markupSig) detect(in []byte) bool {
|
||||
if len(in) < len(hSig)+1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// perform case insensitive check
|
||||
for i, b := range hSig {
|
||||
db := in[i]
|
||||
if 'A' <= b && b <= 'Z' {
|
||||
db &= 0xDF
|
||||
}
|
||||
if b != db {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Next byte must be space or right angle bracket.
|
||||
if db := in[len(hSig)]; db != ' ' && db != '>' {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Implement sig interface.
|
||||
func (tSig ciSig) detect(in []byte) bool {
|
||||
if len(in) < len(tSig)+1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// perform case insensitive check
|
||||
for i, b := range tSig {
|
||||
db := in[i]
|
||||
if 'A' <= b && b <= 'Z' {
|
||||
db &= 0xDF
|
||||
}
|
||||
if b != db {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// a valid shebang starts with the "#!" characters
|
||||
// followed by any number of spaces
|
||||
// followed by the path to the interpreter and optionally, the args for the interpreter
|
||||
func (sSig shebangSig) detect(in []byte) bool {
|
||||
in = firstLine(in)
|
||||
|
||||
if len(in) < len(sSig)+2 {
|
||||
return false
|
||||
}
|
||||
if in[0] != '#' || in[1] != '!' {
|
||||
return false
|
||||
}
|
||||
|
||||
in = trimLWS(trimRWS(in[2:]))
|
||||
|
||||
return bytes.Equal(in, sSig)
|
||||
}
|
||||
|
||||
// Implement sig interface.
|
||||
func (fSig ftypSig) detect(in []byte) bool {
|
||||
return len(in) > 12 &&
|
||||
bytes.Equal(in[4:8], []byte("ftyp")) &&
|
||||
bytes.Equal(in[8:12], fSig)
|
||||
}
|
||||
|
||||
func (xSig xmlSig) detect(in []byte) bool {
|
||||
l := 512
|
||||
if len(in) < l {
|
||||
l = len(in)
|
||||
}
|
||||
in = in[:l]
|
||||
|
||||
if len(xSig.localName) == 0 {
|
||||
return bytes.Index(in, xSig.xmlns) > 0
|
||||
}
|
||||
if len(xSig.xmlns) == 0 {
|
||||
return bytes.Index(in, xSig.localName) > 0
|
||||
}
|
||||
|
||||
localNameIndex := bytes.Index(in, xSig.localName)
|
||||
return localNameIndex != -1 && localNameIndex < bytes.Index(in, xSig.xmlns)
|
||||
}
|
||||
|
||||
func detect(in []byte, sigs []sig) bool {
|
||||
for _, sig := range sigs {
|
||||
if sig.detect(in) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gabriel-vasile/mimetype/internal/json"
|
||||
)
|
||||
|
||||
var (
|
||||
htmlSigs = []sig{
|
||||
markupSig("<!DOCTYPE HTML"),
|
||||
markupSig("<HTML"),
|
||||
markupSig("<HEAD"),
|
||||
markupSig("<SCRIPT"),
|
||||
markupSig("<IFRAME"),
|
||||
markupSig("<H1"),
|
||||
markupSig("<DIV"),
|
||||
markupSig("<FONT"),
|
||||
markupSig("<TABLE"),
|
||||
markupSig("<A"),
|
||||
markupSig("<STYLE"),
|
||||
markupSig("<TITLE"),
|
||||
markupSig("<B"),
|
||||
markupSig("<BODY"),
|
||||
markupSig("<BR"),
|
||||
markupSig("<P"),
|
||||
markupSig("<!--"),
|
||||
}
|
||||
xmlSigs = []sig{
|
||||
markupSig("<?XML"),
|
||||
}
|
||||
rssSigs = []sig{
|
||||
newXmlSig("rss", ""),
|
||||
}
|
||||
atomSigs = []sig{
|
||||
newXmlSig("feed", `xmlns="http://www.w3.org/2005/Atom"`),
|
||||
}
|
||||
kmlSigs = []sig{
|
||||
newXmlSig("kml", `xmlns="http://www.opengis.net/kml/2.2"`),
|
||||
newXmlSig("kml", `xmlns="http://earth.google.com/kml/2.0"`),
|
||||
newXmlSig("kml", `xmlns="http://earth.google.com/kml/2.1"`),
|
||||
newXmlSig("kml", `xmlns="http://earth.google.com/kml/2.2"`),
|
||||
}
|
||||
xliffSigs = []sig{
|
||||
newXmlSig("xliff", `xmlns="urn:oasis:names:tc:xliff:document:1.2"`),
|
||||
}
|
||||
colladaSigs = []sig{
|
||||
newXmlSig("COLLADA", `xmlns="http://www.collada.org/2005/11/COLLADASchema"`),
|
||||
}
|
||||
gmlSigs = []sig{
|
||||
newXmlSig("", `xmlns:gml="http://www.opengis.net/gml"`),
|
||||
newXmlSig("", `xmlns:gml="http://www.opengis.net/gml/3.2"`),
|
||||
newXmlSig("", `xmlns:gml="http://www.opengis.net/gml/3.3/exr"`),
|
||||
}
|
||||
gpxSigs = []sig{
|
||||
newXmlSig("gpx", `xmlns="http://www.topografix.com/GPX/1/1"`),
|
||||
}
|
||||
tcxSigs = []sig{
|
||||
newXmlSig("TrainingCenterDatabase", `xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"`),
|
||||
}
|
||||
x3dSigs = []sig{
|
||||
newXmlSig("X3D", `xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"`),
|
||||
}
|
||||
amfSigs = []sig{
|
||||
newXmlSig("amf", ""),
|
||||
}
|
||||
threemfSigs = []sig{
|
||||
newXmlSig("model", `xmlns="http://schemas.microsoft.com/3dmanufacturing/core/2015/02"`),
|
||||
}
|
||||
vCardSigs = []sig{
|
||||
ciSig("BEGIN:VCARD\n"),
|
||||
}
|
||||
phpSigs = []sig{
|
||||
ciSig("<?PHP"),
|
||||
ciSig("<?\n"),
|
||||
ciSig("<?\r"),
|
||||
ciSig("<? "),
|
||||
shebangSig("/usr/local/bin/php"),
|
||||
shebangSig("/usr/bin/php"),
|
||||
shebangSig("/usr/bin/env php"),
|
||||
}
|
||||
jsSigs = []sig{
|
||||
shebangSig("/bin/node"),
|
||||
shebangSig("/usr/bin/node"),
|
||||
shebangSig("/bin/nodejs"),
|
||||
shebangSig("/usr/bin/nodejs"),
|
||||
shebangSig("/usr/bin/env node"),
|
||||
shebangSig("/usr/bin/env nodejs"),
|
||||
}
|
||||
luaSigs = []sig{
|
||||
shebangSig("/usr/bin/lua"),
|
||||
shebangSig("/usr/local/bin/lua"),
|
||||
shebangSig("/usr/bin/env lua"),
|
||||
}
|
||||
perlSigs = []sig{
|
||||
shebangSig("/usr/bin/perl"),
|
||||
shebangSig("/usr/bin/env perl"),
|
||||
}
|
||||
pythonSigs = []sig{
|
||||
shebangSig("/usr/bin/python"),
|
||||
shebangSig("/usr/local/bin/python"),
|
||||
shebangSig("/usr/bin/env python"),
|
||||
}
|
||||
tclSigs = []sig{
|
||||
shebangSig("/usr/bin/tcl"),
|
||||
shebangSig("/usr/local/bin/tcl"),
|
||||
shebangSig("/usr/bin/env tcl"),
|
||||
shebangSig("/usr/bin/tclsh"),
|
||||
shebangSig("/usr/local/bin/tclsh"),
|
||||
shebangSig("/usr/bin/env tclsh"),
|
||||
shebangSig("/usr/bin/wish"),
|
||||
shebangSig("/usr/local/bin/wish"),
|
||||
shebangSig("/usr/bin/env wish"),
|
||||
}
|
||||
)
|
||||
|
||||
// Txt matches a text file.
|
||||
func Txt(in []byte) bool {
|
||||
in = trimLWS(in)
|
||||
for _, b := range in {
|
||||
if b <= 0x08 ||
|
||||
b == 0x0B ||
|
||||
0x0E <= b && b <= 0x1A ||
|
||||
0x1C <= b && b <= 0x1F {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Html matches a Hypertext Markup Language file.
|
||||
func Html(in []byte) bool {
|
||||
return detect(in, htmlSigs)
|
||||
}
|
||||
|
||||
// Xml matches an Extensible Markup Language file.
|
||||
func Xml(in []byte) bool {
|
||||
return detect(in, xmlSigs)
|
||||
}
|
||||
|
||||
// Php matches a PHP: Hypertext Preprocessor file.
|
||||
func Php(in []byte) bool {
|
||||
return detect(in, phpSigs)
|
||||
}
|
||||
|
||||
// Json matches a JavaScript Object Notation file.
|
||||
func Json(in []byte) bool {
|
||||
parsed, err := json.Scan(in)
|
||||
if len(in) < ReadLimit {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
return parsed == len(in)
|
||||
}
|
||||
|
||||
// GeoJson matches a RFC 7946 GeoJSON file.
|
||||
//
|
||||
// BUG(gabriel-vasile): The "type" key should be searched for in the root object.
|
||||
func GeoJson(in []byte) bool {
|
||||
if len(in) == 0 {
|
||||
return false
|
||||
}
|
||||
in = trimLWS(in)
|
||||
// geojson is always an object
|
||||
if in[0] != '{' {
|
||||
return false
|
||||
}
|
||||
|
||||
s := []byte(`"type"`)
|
||||
si := bytes.Index(in, s)
|
||||
sl := len(s)
|
||||
|
||||
if si == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// if the "type" string is the suffix of the input
|
||||
// there is no need to search for the value of the key
|
||||
if si+sl == len(in) {
|
||||
return false
|
||||
}
|
||||
// skip the "type" part
|
||||
in = in[si+sl:]
|
||||
// skip any whitespace before the colon
|
||||
in = trimLWS(in)
|
||||
// skip any whitesapce after the colon
|
||||
// not checking if char is colon because json matcher already did check
|
||||
in = trimLWS(in[1:])
|
||||
|
||||
geoJsonTypes := [][]byte{
|
||||
[]byte(`"Feature"`),
|
||||
[]byte(`"FeatureCollection"`),
|
||||
[]byte(`"Point"`),
|
||||
[]byte(`"LineString"`),
|
||||
[]byte(`"Polygon"`),
|
||||
[]byte(`"MultiPoint"`),
|
||||
[]byte(`"MultiLineString"`),
|
||||
[]byte(`"MultiPolygon"`),
|
||||
[]byte(`"GeometryCollection"`),
|
||||
}
|
||||
for _, t := range geoJsonTypes {
|
||||
if bytes.HasPrefix(in, t) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Js matches a Javascript file.
|
||||
func Js(in []byte) bool {
|
||||
return detect(in, jsSigs)
|
||||
}
|
||||
|
||||
// Lua matches a Lua programming language file.
|
||||
func Lua(in []byte) bool {
|
||||
return detect(in, luaSigs)
|
||||
}
|
||||
|
||||
// Perl matches a Perl programming language file.
|
||||
func Perl(in []byte) bool {
|
||||
return detect(in, perlSigs)
|
||||
}
|
||||
|
||||
// Python matches a Python programming language file.
|
||||
func Python(in []byte) bool {
|
||||
return detect(in, pythonSigs)
|
||||
}
|
||||
|
||||
// Tcl matches a Tcl programming language file.
|
||||
func Tcl(in []byte) bool {
|
||||
return detect(in, tclSigs)
|
||||
}
|
||||
|
||||
// Rtf matches a Rich Text Format file.
|
||||
func Rtf(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("{\\rtf1"))
|
||||
}
|
||||
|
||||
// Svg matches a SVG file.
|
||||
func Svg(in []byte) bool {
|
||||
return bytes.Contains(in, []byte("<svg"))
|
||||
}
|
||||
|
||||
// Rss matches a Rich Site Summary file.
|
||||
func Rss(in []byte) bool {
|
||||
return detect(in, rssSigs)
|
||||
}
|
||||
|
||||
// Atom matches an Atom Syndication Format file.
|
||||
func Atom(in []byte) bool {
|
||||
return detect(in, atomSigs)
|
||||
}
|
||||
|
||||
// Kml matches a Keyhole Markup Language file.
|
||||
func Kml(in []byte) bool {
|
||||
return detect(in, kmlSigs)
|
||||
}
|
||||
|
||||
// Xliff matches a XML Localization Interchange File Format file.
|
||||
func Xliff(in []byte) bool {
|
||||
return detect(in, xliffSigs)
|
||||
}
|
||||
|
||||
// Collada matches a COLLAborative Design Activity file.
|
||||
func Collada(in []byte) bool {
|
||||
return detect(in, colladaSigs)
|
||||
}
|
||||
|
||||
// Gml matches a Geography Markup Language file.
|
||||
func Gml(in []byte) bool {
|
||||
return detect(in, gmlSigs)
|
||||
}
|
||||
|
||||
// Gpx matches a GPS Exchange Format file.
|
||||
func Gpx(in []byte) bool {
|
||||
return detect(in, gpxSigs)
|
||||
}
|
||||
|
||||
// Tcx matches a Training Center XML file.
|
||||
func Tcx(in []byte) bool {
|
||||
return detect(in, tcxSigs)
|
||||
}
|
||||
|
||||
// Amf matches an Additive Manufacturing XML file.
|
||||
func Amf(in []byte) bool {
|
||||
return detect(in, amfSigs)
|
||||
}
|
||||
|
||||
// Threemf matches a 3D Manufacturing Format file.
|
||||
func Threemf(in []byte) bool {
|
||||
return detect(in, threemfSigs)
|
||||
}
|
||||
|
||||
// X3d matches an Extensible 3D Graphics file.
|
||||
func X3d(in []byte) bool {
|
||||
return detect(in, x3dSigs)
|
||||
}
|
||||
|
||||
// VCard matches a Virtual Contact File.
|
||||
func VCard(in []byte) bool {
|
||||
return detect(in, vCardSigs)
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Csv matches a comma-separated values file.
|
||||
func Csv(in []byte) bool {
|
||||
return sv(in, ',')
|
||||
}
|
||||
|
||||
// Tsv matches a tab-separated values file.
|
||||
func Tsv(in []byte) bool {
|
||||
return sv(in, '\t')
|
||||
}
|
||||
|
||||
func sv(in []byte, comma rune) bool {
|
||||
r := csv.NewReader(butLastLineReader(in, ReadLimit))
|
||||
r.Comma = comma
|
||||
r.TrimLeadingSpace = true
|
||||
r.LazyQuotes = true
|
||||
r.Comment = '#'
|
||||
|
||||
lines, err := r.ReadAll()
|
||||
return err == nil && r.FieldsPerRecord > 1 && len(lines) > 1
|
||||
}
|
||||
|
||||
// butLastLineReader returns a reader to the provided byte slice.
|
||||
// the reader is guaranteed to reach EOF before it reads `cutAt` bytes.
|
||||
// bytes after the last newline are dropped from the input.
|
||||
func butLastLineReader(in []byte, cutAt int) io.Reader {
|
||||
if len(in) >= cutAt {
|
||||
for i := cutAt - 1; i > 0; i-- {
|
||||
if in[i] == '\n' {
|
||||
return bytes.NewReader(in[:i])
|
||||
}
|
||||
}
|
||||
|
||||
// no newline was found between the 0 index and cutAt
|
||||
return bytes.NewReader(in[:cutAt])
|
||||
}
|
||||
|
||||
return bytes.NewReader(in)
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// WebM matches a WebM file.
|
||||
func WebM(in []byte) bool {
|
||||
return isMatroskaFileTypeMatched(in, "webm")
|
||||
}
|
||||
|
||||
// Mkv matches a mkv file.
|
||||
func Mkv(in []byte) bool {
|
||||
return isMatroskaFileTypeMatched(in, "matroska")
|
||||
}
|
||||
|
||||
// isMatroskaFileTypeMatched is used for webm and mkv file matching.
|
||||
// It checks for .Eߣ sequence. If the sequence is found,
|
||||
// then it means it is Matroska media container, including WebM.
|
||||
// Then it verifies which of the file type it is representing by matching the
|
||||
// file specific string.
|
||||
func isMatroskaFileTypeMatched(in []byte, flType string) bool {
|
||||
if bytes.HasPrefix(in, []byte("\x1A\x45\xDF\xA3")) {
|
||||
return isFileTypeNamePresent(in, flType)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isFileTypeNamePresent accepts the matroska input data stream and searches
|
||||
// for the given file type in the stream. Return whether a match is found.
|
||||
// The logic of search is: find first instance of \x42\x82 and then
|
||||
// search for given string after one byte of above instance.
|
||||
func isFileTypeNamePresent(in []byte, flType string) bool {
|
||||
var ind int
|
||||
if len(in) >= 4096 { // restricting length to 4096
|
||||
ind = bytes.Index(in[0:4096], []byte("\x42\x82"))
|
||||
} else {
|
||||
ind = bytes.Index(in, []byte("\x42\x82"))
|
||||
}
|
||||
if ind > 0 {
|
||||
// filetype name will be present exactly
|
||||
// one byte after the match of the two bytes "\x42\x82"
|
||||
return bytes.HasPrefix(in[ind+3:], []byte(flType))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Flv matches a Flash video file.
|
||||
func Flv(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte("\x46\x4C\x56\x01"))
|
||||
}
|
||||
|
||||
// Mpeg matches a Moving Picture Experts Group file.
|
||||
func Mpeg(in []byte) bool {
|
||||
return bytes.HasPrefix(in, []byte{0x00, 0x00, 0x01}) &&
|
||||
in[3] >= 0xB0 && in[3] <= 0xBF
|
||||
}
|
||||
|
||||
// Avi matches an Audio Video Interleaved file.
|
||||
func Avi(in []byte) bool {
|
||||
return len(in) > 16 &&
|
||||
bytes.Equal(in[:4], []byte("RIFF")) &&
|
||||
bytes.Equal(in[8:16], []byte("AVI LIST"))
|
||||
}
|
||||
|
||||
// Asf matches an Advanced Systems Format file.
|
||||
func Asf(in []byte) bool {
|
||||
return len(in) > 16 && bytes.Equal(in[:16], []byte{0x30, 0x26, 0xB2, 0x75,
|
||||
0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C})
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package matchers
|
||||
|
||||
var (
|
||||
mp4Sigs = []sig{
|
||||
ftypSig("avc1"), ftypSig("dash"), ftypSig("iso2"), ftypSig("iso3"),
|
||||
ftypSig("iso4"), ftypSig("iso5"), ftypSig("iso6"), ftypSig("isom"),
|
||||
ftypSig("mmp4"), ftypSig("mp41"), ftypSig("mp42"), ftypSig("mp4v"),
|
||||
ftypSig("mp71"), ftypSig("MSNV"), ftypSig("NDAS"), ftypSig("NDSC"),
|
||||
ftypSig("NSDC"), ftypSig("NSDH"), ftypSig("NDSM"), ftypSig("NDSP"),
|
||||
ftypSig("NDSS"), ftypSig("NDXC"), ftypSig("NDXH"), ftypSig("NDXM"),
|
||||
ftypSig("NDXP"), ftypSig("NDXS"), ftypSig("F4V "), ftypSig("F4P "),
|
||||
}
|
||||
threeGPSigs = []sig{
|
||||
ftypSig("3gp1"), ftypSig("3gp2"), ftypSig("3gp3"), ftypSig("3gp4"),
|
||||
ftypSig("3gp5"), ftypSig("3gp6"), ftypSig("3gs7"), ftypSig("3ge6"),
|
||||
ftypSig("3ge7"), ftypSig("3gg6"),
|
||||
}
|
||||
threeG2Sigs = []sig{
|
||||
ftypSig("3g2a"), ftypSig("3g2b"), ftypSig("3g2c"), ftypSig("KDDI"),
|
||||
}
|
||||
amp4Sigs = []sig{
|
||||
// audio for Adobe Flash Player 9+
|
||||
ftypSig("F4A "), ftypSig("F4B "),
|
||||
// Apple iTunes AAC-LC (.M4A) Audio
|
||||
ftypSig("M4B "), ftypSig("M4P "),
|
||||
// MPEG-4 (.MP4) for SonyPSP
|
||||
ftypSig("MSNV"),
|
||||
// Nero Digital AAC Audio
|
||||
ftypSig("NDAS"),
|
||||
}
|
||||
qtSigs = []sig{ftypSig("qt "), ftypSig("moov")}
|
||||
mqvSigs = []sig{ftypSig("mqt ")}
|
||||
m4aSigs = []sig{ftypSig("M4A ")}
|
||||
// TODO: add support for remaining video formats at ftyps.com.
|
||||
)
|
||||
|
||||
// Mp4 matches an MP4 file.
|
||||
func Mp4(in []byte) bool {
|
||||
return detect(in, mp4Sigs)
|
||||
}
|
||||
|
||||
// ThreeGP matches a 3GPP file.
|
||||
func ThreeGP(in []byte) bool {
|
||||
return detect(in, threeGPSigs)
|
||||
}
|
||||
|
||||
// ThreeG2 matches a 3GPP2 file.
|
||||
func ThreeG2(in []byte) bool {
|
||||
return detect(in, threeG2Sigs)
|
||||
}
|
||||
|
||||
// AMp4 matches an audio MP4 file.
|
||||
func AMp4(in []byte) bool {
|
||||
return detect(in, amp4Sigs)
|
||||
}
|
||||
|
||||
// QuickTime matches a QuickTime File Format file.
|
||||
func QuickTime(in []byte) bool {
|
||||
return detect(in, qtSigs)
|
||||
}
|
||||
|
||||
// Mqv matches a Sony / Mobile QuickTime file.
|
||||
func Mqv(in []byte) bool {
|
||||
return detect(in, mqvSigs)
|
||||
}
|
||||
|
||||
// M4a matches an audio M4A file.
|
||||
func M4a(in []byte) bool {
|
||||
return detect(in, m4aSigs)
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Package mimetype uses magic number signatures
|
||||
// to detect the MIME type and extension of a file.
|
||||
package mimetype
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gabriel-vasile/mimetype/internal/matchers"
|
||||
)
|
||||
|
||||
// Detect returns the MIME type and extension of the provided byte slice.
|
||||
//
|
||||
// mime is always a valid MIME type, with application/octet-stream as fallback.
|
||||
// extension is empty string if detected file format does not have an extension.
|
||||
func Detect(in []byte) (mime, extension string) {
|
||||
if len(in) == 0 {
|
||||
return "inode/x-empty", ""
|
||||
}
|
||||
n := root.match(in, root)
|
||||
return n.mime, n.extension
|
||||
}
|
||||
|
||||
// DetectReader returns the MIME type and extension
|
||||
// of the byte slice read from the provided reader.
|
||||
//
|
||||
// mime is always a valid MIME type, with application/octet-stream as fallback.
|
||||
// extension is empty string if detection failed with an error or
|
||||
// detected file format does not have an extension.
|
||||
func DetectReader(r io.Reader) (mime, extension string, err error) {
|
||||
in := make([]byte, matchers.ReadLimit)
|
||||
n, err := r.Read(in)
|
||||
if err != nil && err != io.EOF {
|
||||
return root.mime, root.extension, err
|
||||
}
|
||||
in = in[:n]
|
||||
|
||||
mime, extension = Detect(in)
|
||||
return mime, extension, nil
|
||||
}
|
||||
|
||||
// DetectFile returns the MIME type and extension of the provided file.
|
||||
//
|
||||
// mime is always a valid MIME type, with application/octet-stream as fallback.
|
||||
// extension is empty string if detection failed with an error or
|
||||
// detected file format does not have an extension.
|
||||
func DetectFile(file string) (mime, extension string, err error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return root.mime, root.extension, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return DetectReader(f)
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 744 KiB |
+43
@@ -0,0 +1,43 @@
|
||||
package mimetype
|
||||
|
||||
type (
|
||||
// node represents a vertex in the matchers tree structure.
|
||||
// It holds the mime type, the extension and the function
|
||||
// to check whether a byte slice has the mime type.
|
||||
node struct {
|
||||
mime string
|
||||
extension string
|
||||
matchFunc func([]byte) bool
|
||||
children []*node
|
||||
}
|
||||
)
|
||||
|
||||
func newNode(mime, extension string, matchFunc func([]byte) bool, children ...*node) *node {
|
||||
return &node{
|
||||
mime: mime,
|
||||
extension: extension,
|
||||
matchFunc: matchFunc,
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
|
||||
// match does a depth-first search on the matchers tree.
|
||||
// it returns the deepest successful matcher for which all the children fail.
|
||||
func (n *node) match(in []byte, deepestMatch *node) *node {
|
||||
for _, c := range n.children {
|
||||
if c.matchFunc(in) {
|
||||
return c.match(in, c)
|
||||
}
|
||||
}
|
||||
|
||||
return deepestMatch
|
||||
}
|
||||
|
||||
func (n *node) flatten() []*node {
|
||||
out := []*node{n}
|
||||
for _, c := range n.children {
|
||||
out = append(out, c.flatten()...)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
## 114 Supported MIME types
|
||||
This file is automatically generated when running tests. Do not edit manually.
|
||||
|
||||
Extension | MIME type
|
||||
--------- | --------
|
||||
**n/a** | application/octet-stream
|
||||
**7z** | application/x-7z-compressed
|
||||
**zip** | application/zip
|
||||
**xlsx** | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
**docx** | application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
**pptx** | application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
**epub** | application/epub+zip
|
||||
**jar** | application/jar
|
||||
**odt** | application/vnd.oasis.opendocument.text
|
||||
**ott** | application/vnd.oasis.opendocument.text-template
|
||||
**ods** | application/vnd.oasis.opendocument.spreadsheet
|
||||
**ots** | application/vnd.oasis.opendocument.spreadsheet-template
|
||||
**odp** | application/vnd.oasis.opendocument.presentation
|
||||
**otp** | application/vnd.oasis.opendocument.presentation-template
|
||||
**odg** | application/vnd.oasis.opendocument.graphics
|
||||
**otg** | application/vnd.oasis.opendocument.graphics-template
|
||||
**odf** | application/vnd.oasis.opendocument.formula
|
||||
**pdf** | application/pdf
|
||||
**xls** | application/vnd.ms-excel
|
||||
**ppt** | application/vnd.ms-powerpoint
|
||||
**doc** | application/msword
|
||||
**ps** | application/postscript
|
||||
**psd** | application/x-photoshop
|
||||
**ogg** | application/ogg
|
||||
**png** | image/png
|
||||
**jpg** | image/jpeg
|
||||
**gif** | image/gif
|
||||
**webp** | image/webp
|
||||
**exe** | application/vnd.microsoft.portable-executable
|
||||
**n/a** | application/x-elf
|
||||
**n/a** | application/x-object
|
||||
**n/a** | application/x-executable
|
||||
**so** | application/x-sharedlib
|
||||
**n/a** | application/x-coredump
|
||||
**a** | application/x-archive
|
||||
**deb** | application/vnd.debian.binary-package
|
||||
**tar** | application/x-tar
|
||||
**xar** | application/x-xar
|
||||
**bz2** | application/x-bzip2
|
||||
**fits** | application/fits
|
||||
**tiff** | image/tiff
|
||||
**bmp** | image/bmp
|
||||
**ico** | image/x-icon
|
||||
**mp3** | audio/mpeg
|
||||
**flac** | audio/flac
|
||||
**midi** | audio/midi
|
||||
**ape** | audio/ape
|
||||
**mpc** | audio/musepack
|
||||
**amr** | audio/amr
|
||||
**wav** | audio/wav
|
||||
**aiff** | audio/aiff
|
||||
**au** | audio/basic
|
||||
**mpeg** | video/mpeg
|
||||
**mov** | video/quicktime
|
||||
**mqv** | video/quicktime
|
||||
**mp4** | video/mp4
|
||||
**webm** | video/webm
|
||||
**3gp** | video/3gpp
|
||||
**3g2** | video/3gpp2
|
||||
**avi** | video/x-msvideo
|
||||
**flv** | video/x-flv
|
||||
**mkv** | video/x-matroska
|
||||
**asf** | video/x-ms-asf
|
||||
**aac** | audio/aac
|
||||
**voc** | audio/x-unknown
|
||||
**mp4** | audio/mp4
|
||||
**m4a** | audio/x-m4a
|
||||
**txt** | text/plain
|
||||
**html** | text/html; charset=utf-8
|
||||
**svg** | image/svg+xml
|
||||
**xml** | text/xml; charset=utf-8
|
||||
**rss** | application/rss+xml
|
||||
**atom** | application/atom+xml
|
||||
**x3d** | model/x3d+xml
|
||||
**kml** | application/vnd.google-earth.kml+xml
|
||||
**xlf** | application/x-xliff+xml
|
||||
**dae** | model/vnd.collada+xml
|
||||
**gml** | application/gml+xml
|
||||
**gpx** | application/gpx+xml
|
||||
**tcx** | application/vnd.garmin.tcx+xml
|
||||
**amf** | application/x-amf
|
||||
**3mf** | application/vnd.ms-package.3dmanufacturing-3dmodel+xml
|
||||
**php** | text/x-php; charset=utf-8
|
||||
**js** | application/javascript
|
||||
**lua** | text/x-lua
|
||||
**pl** | text/x-perl
|
||||
**py** | application/x-python
|
||||
**json** | application/json
|
||||
**geojson** | application/geo+json
|
||||
**rtf** | text/rtf
|
||||
**tcl** | text/x-tcl
|
||||
**csv** | text/csv
|
||||
**tsv** | text/tab-separated-values
|
||||
**vcf** | text/vcard
|
||||
**gz** | application/gzip
|
||||
**class** | application/x-java-applet; charset=binary
|
||||
**swf** | application/x-shockwave-flash
|
||||
**crx** | application/x-chrome-extension
|
||||
**woff** | font/woff
|
||||
**woff2** | font/woff2
|
||||
**otf** | font/otf
|
||||
**eot** | application/vnd.ms-fontobject
|
||||
**wasm** | application/wasm
|
||||
**shx** | application/octet-stream
|
||||
**shp** | application/octet-stream
|
||||
**dbf** | application/x-dbf
|
||||
**dcm** | application/dicom
|
||||
**rar** | application/x-rar-compressed
|
||||
**djvu** | image/vnd.djvu
|
||||
**mobi** | application/x-mobipocket-ebook
|
||||
**lit** | application/x-ms-reader
|
||||
**bpg** | image/bpg
|
||||
**sqlite** | application/x-sqlite3
|
||||
**dwg** | image/vnd.dwg
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package mimetype
|
||||
|
||||
import "github.com/gabriel-vasile/mimetype/internal/matchers"
|
||||
|
||||
// root is a matcher which passes for any slice of bytes.
|
||||
// When a matcher passes the check, the children matchers
|
||||
// are tried in order to find a more accurate mime type.
|
||||
var root = newNode("application/octet-stream", "", matchers.True,
|
||||
sevenZ, zip, pdf, xls, ppt, doc, ps, psd, ogg, png, jpg, gif, webp, exe, elf,
|
||||
ar, tar, xar, bz2, fits, tiff, bmp, ico, mp3, flac, midi, ape, musePack, amr,
|
||||
wav, aiff, au, mpeg, quickTime, mqv, mp4, webM, threeGP, threeG2, avi, flv,
|
||||
mkv, asf, aac, voc, aMp4, m4a, txt, gzip, class, swf, crx, woff, woff2, otf,
|
||||
eot, wasm, shx, dbf, dcm, rar, djvu, mobi, lit, bpg, sqlite3, dwg,
|
||||
)
|
||||
|
||||
// The list of nodes appended to the root node
|
||||
var (
|
||||
gzip = newNode("application/gzip", "gz", matchers.Gzip)
|
||||
sevenZ = newNode("application/x-7z-compressed", "7z", matchers.SevenZ)
|
||||
zip = newNode("application/zip", "zip", matchers.Zip, xlsx, docx, pptx, epub, jar, odt, ods, odp, odg, odf)
|
||||
tar = newNode("application/x-tar", "tar", matchers.Tar)
|
||||
xar = newNode("application/x-xar", "xar", matchers.Xar)
|
||||
bz2 = newNode("application/x-bzip2", "bz2", matchers.Bz2)
|
||||
pdf = newNode("application/pdf", "pdf", matchers.Pdf)
|
||||
xlsx = newNode("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx", matchers.Xlsx)
|
||||
docx = newNode("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx", matchers.Docx)
|
||||
pptx = newNode("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx", matchers.Pptx)
|
||||
epub = newNode("application/epub+zip", "epub", matchers.Epub)
|
||||
jar = newNode("application/jar", "jar", matchers.Jar)
|
||||
doc = newNode("application/msword", "doc", matchers.Doc)
|
||||
ppt = newNode("application/vnd.ms-powerpoint", "ppt", matchers.Ppt)
|
||||
xls = newNode("application/vnd.ms-excel", "xls", matchers.Xls)
|
||||
ps = newNode("application/postscript", "ps", matchers.Ps)
|
||||
psd = newNode("application/x-photoshop", "psd", matchers.Psd)
|
||||
fits = newNode("application/fits", "fits", matchers.Fits)
|
||||
ogg = newNode("application/ogg", "ogg", matchers.Ogg)
|
||||
txt = newNode("text/plain", "txt", matchers.Txt, html, svg, xml, php, js, lua, perl, python, json, rtf, tcl, csv, tsv, vCard)
|
||||
xml = newNode("text/xml; charset=utf-8", "xml", matchers.Xml, rss, atom, x3d, kml, xliff, collada, gml, gpx, tcx, amf, threemf)
|
||||
json = newNode("application/json", "json", matchers.Json, geoJson)
|
||||
csv = newNode("text/csv", "csv", matchers.Csv)
|
||||
tsv = newNode("text/tab-separated-values", "tsv", matchers.Tsv)
|
||||
geoJson = newNode("application/geo+json", "geojson", matchers.GeoJson)
|
||||
html = newNode("text/html; charset=utf-8", "html", matchers.Html)
|
||||
php = newNode("text/x-php; charset=utf-8", "php", matchers.Php)
|
||||
rtf = newNode("text/rtf", "rtf", matchers.Rtf)
|
||||
js = newNode("application/javascript", "js", matchers.Js)
|
||||
lua = newNode("text/x-lua", "lua", matchers.Lua)
|
||||
perl = newNode("text/x-perl", "pl", matchers.Perl)
|
||||
python = newNode("application/x-python", "py", matchers.Python)
|
||||
tcl = newNode("text/x-tcl", "tcl", matchers.Tcl)
|
||||
vCard = newNode("text/vcard", "vcf", matchers.VCard)
|
||||
svg = newNode("image/svg+xml", "svg", matchers.Svg)
|
||||
rss = newNode("application/rss+xml", "rss", matchers.Rss)
|
||||
atom = newNode("application/atom+xml", "atom", matchers.Atom)
|
||||
x3d = newNode("model/x3d+xml", "x3d", matchers.X3d)
|
||||
kml = newNode("application/vnd.google-earth.kml+xml", "kml", matchers.Kml)
|
||||
xliff = newNode("application/x-xliff+xml", "xlf", matchers.Xliff)
|
||||
collada = newNode("model/vnd.collada+xml", "dae", matchers.Collada)
|
||||
gml = newNode("application/gml+xml", "gml", matchers.Gml)
|
||||
gpx = newNode("application/gpx+xml", "gpx", matchers.Gpx)
|
||||
tcx = newNode("application/vnd.garmin.tcx+xml", "tcx", matchers.Tcx)
|
||||
amf = newNode("application/x-amf", "amf", matchers.Amf)
|
||||
threemf = newNode("application/vnd.ms-package.3dmanufacturing-3dmodel+xml", "3mf", matchers.Threemf)
|
||||
png = newNode("image/png", "png", matchers.Png)
|
||||
jpg = newNode("image/jpeg", "jpg", matchers.Jpg)
|
||||
bpg = newNode("image/bpg", "bpg", matchers.Bpg)
|
||||
gif = newNode("image/gif", "gif", matchers.Gif)
|
||||
webp = newNode("image/webp", "webp", matchers.Webp)
|
||||
tiff = newNode("image/tiff", "tiff", matchers.Tiff)
|
||||
bmp = newNode("image/bmp", "bmp", matchers.Bmp)
|
||||
ico = newNode("image/x-icon", "ico", matchers.Ico)
|
||||
mp3 = newNode("audio/mpeg", "mp3", matchers.Mp3)
|
||||
flac = newNode("audio/flac", "flac", matchers.Flac)
|
||||
midi = newNode("audio/midi", "midi", matchers.Midi)
|
||||
ape = newNode("audio/ape", "ape", matchers.Ape)
|
||||
musePack = newNode("audio/musepack", "mpc", matchers.MusePack)
|
||||
wav = newNode("audio/wav", "wav", matchers.Wav)
|
||||
aiff = newNode("audio/aiff", "aiff", matchers.Aiff)
|
||||
au = newNode("audio/basic", "au", matchers.Au)
|
||||
amr = newNode("audio/amr", "amr", matchers.Amr)
|
||||
aac = newNode("audio/aac", "aac", matchers.Aac)
|
||||
voc = newNode("audio/x-unknown", "voc", matchers.Voc)
|
||||
aMp4 = newNode("audio/mp4", "mp4", matchers.AMp4)
|
||||
m4a = newNode("audio/x-m4a", "m4a", matchers.M4a)
|
||||
mp4 = newNode("video/mp4", "mp4", matchers.Mp4)
|
||||
webM = newNode("video/webm", "webm", matchers.WebM)
|
||||
mpeg = newNode("video/mpeg", "mpeg", matchers.Mpeg)
|
||||
quickTime = newNode("video/quicktime", "mov", matchers.QuickTime)
|
||||
mqv = newNode("video/quicktime", "mqv", matchers.Mqv)
|
||||
threeGP = newNode("video/3gpp", "3gp", matchers.ThreeGP)
|
||||
threeG2 = newNode("video/3gpp2", "3g2", matchers.ThreeG2)
|
||||
avi = newNode("video/x-msvideo", "avi", matchers.Avi)
|
||||
flv = newNode("video/x-flv", "flv", matchers.Flv)
|
||||
mkv = newNode("video/x-matroska", "mkv", matchers.Mkv)
|
||||
asf = newNode("video/x-ms-asf", "asf", matchers.Asf)
|
||||
class = newNode("application/x-java-applet; charset=binary", "class", matchers.Class)
|
||||
swf = newNode("application/x-shockwave-flash", "swf", matchers.Swf)
|
||||
crx = newNode("application/x-chrome-extension", "crx", matchers.Crx)
|
||||
woff = newNode("font/woff", "woff", matchers.Woff)
|
||||
woff2 = newNode("font/woff2", "woff2", matchers.Woff2)
|
||||
otf = newNode("font/otf", "otf", matchers.Otf)
|
||||
eot = newNode("application/vnd.ms-fontobject", "eot", matchers.Eot)
|
||||
wasm = newNode("application/wasm", "wasm", matchers.Wasm)
|
||||
shp = newNode("application/octet-stream", "shp", matchers.Shp)
|
||||
shx = newNode("application/octet-stream", "shx", matchers.Shx, shp)
|
||||
dbf = newNode("application/x-dbf", "dbf", matchers.Dbf)
|
||||
exe = newNode("application/vnd.microsoft.portable-executable", "exe", matchers.Exe)
|
||||
elf = newNode("application/x-elf", "", matchers.Elf, elfObj, elfExe, elfLib, elfDump)
|
||||
elfObj = newNode("application/x-object", "", matchers.ElfObj)
|
||||
elfExe = newNode("application/x-executable", "", matchers.ElfExe)
|
||||
elfLib = newNode("application/x-sharedlib", "so", matchers.ElfLib)
|
||||
elfDump = newNode("application/x-coredump", "", matchers.ElfDump)
|
||||
ar = newNode("application/x-archive", "a", matchers.Ar, deb)
|
||||
deb = newNode("application/vnd.debian.binary-package", "deb", matchers.Deb)
|
||||
dcm = newNode("application/dicom", "dcm", matchers.Dcm)
|
||||
odt = newNode("application/vnd.oasis.opendocument.text", "odt", matchers.Odt, ott)
|
||||
ott = newNode("application/vnd.oasis.opendocument.text-template", "ott", matchers.Ott)
|
||||
ods = newNode("application/vnd.oasis.opendocument.spreadsheet", "ods", matchers.Ods, ots)
|
||||
ots = newNode("application/vnd.oasis.opendocument.spreadsheet-template", "ots", matchers.Ots)
|
||||
odp = newNode("application/vnd.oasis.opendocument.presentation", "odp", matchers.Odp, otp)
|
||||
otp = newNode("application/vnd.oasis.opendocument.presentation-template", "otp", matchers.Otp)
|
||||
odg = newNode("application/vnd.oasis.opendocument.graphics", "odg", matchers.Odg, otg)
|
||||
otg = newNode("application/vnd.oasis.opendocument.graphics-template", "otg", matchers.Otg)
|
||||
odf = newNode("application/vnd.oasis.opendocument.formula", "odf", matchers.Odf)
|
||||
rar = newNode("application/x-rar-compressed", "rar", matchers.Rar)
|
||||
djvu = newNode("image/vnd.djvu", "djvu", matchers.DjVu)
|
||||
mobi = newNode("application/x-mobipocket-ebook", "mobi", matchers.Mobi)
|
||||
lit = newNode("application/x-ms-reader", "lit", matchers.Lit)
|
||||
sqlite3 = newNode("application/x-sqlite3", "sqlite", matchers.Sqlite)
|
||||
dwg = newNode("image/vnd.dwg", "dwg", matchers.Dwg)
|
||||
)
|
||||
Reference in New Issue
Block a user