feat: initial commit

This commit is contained in:
Tomas Aparicio
2015-09-24 10:40:41 +01:00
commit 4af2d19f91
18 changed files with 767 additions and 0 deletions

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
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

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
/bimg
/bundle
bin
/*.jpg
/*.png
/*.webp
/fixtures/*_out.*

7
.travis.yml Normal file
View File

@@ -0,0 +1,7 @@
language: go
go:
- 1.5
- 1.4
- 1.3
- release
- tip

24
LICENSE Normal file
View File

@@ -0,0 +1,24 @@
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.

41
README.md Normal file
View File

@@ -0,0 +1,41 @@
# filetype [![Build Status](https://travis-ci.org/h2non/filetype.png)](https://travis-ci.org/h2non/filetype) [![GoDoc](https://godoc.org/github.com/h2non/filetype?status.svg)](https://godoc.org/github.com/h2non/filetype)
Small [Go](https://golang.org) package to infer the file type checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of a given binary buffer.
Supports a wide range of file types, including images formats, fonts, videos, audio and other common application files, and provides the proper file extension and convenient MIME code.
## Installation
```bash
go get gopkg.in/h2non/filetype.v0
```
## Usage
```go
import (
"fmt"
"io/ioutil"
"gopkg.in/h2non/filetype.v0"
)
func main() {
buf, _ := ioutil.ReadFile("sample.jpg")
kind, unkwown := filetype.Type(buf)
if unkwown != nil {
fmt.Printf("Unkwown file type")
return
}
fmt.Printf("File type found: %s. MIME: %s", kind.Extension, kind.MIME.Value)
}
```
## API
## License
MIT - Tomas Aparicio

116
filetype.go Normal file
View File

@@ -0,0 +1,116 @@
package filetype
import (
"errors"
"gopkg.in/h2non/filetype.v0/matchers"
"gopkg.in/h2non/filetype.v0/types"
)
// Map of extensions and file types
var Types = types.Types
// Map of file matchers
var Matchers = matchers.Matchers
// Default types
var Empty = types.Empty
var Unknown = types.Unknown
// Predefined errors
var EmptyBufferErr = errors.New("Empty buffer")
var UnknownBufferErr = errors.New("Unknown buffer type")
func DoMatch(buf []byte) (types.Type, error) {
return matchers.Match(buf)
}
// Infer the file type of a buffer inspecting the magic numbers
func Match(buf []byte) (types.Type, error) {
return DoMatch(buf)
}
// Alias to Match()
func Type(buf []byte) (types.Type, error) {
return Match(buf)
}
func doMatchMap(buf []byte, machers matchers.Map) (types.Type, error) {
kind := matchers.MatchMap(buf, machers)
if kind != types.Unknown {
return kind, nil
}
return kind, UnknownBufferErr
}
// Match file as image type
func Image(buf []byte) (types.Type, error) {
return doMatchMap(buf, matchers.Image)
}
// Match file as audio type
func Audio(buf []byte) (types.Type, error) {
return doMatchMap(buf, matchers.Audio)
}
// Match file as video type
func Video(buf []byte) (types.Type, error) {
return doMatchMap(buf, matchers.Audio)
}
// Match file as text font type
func Font(buf []byte) (types.Type, error) {
return doMatchMap(buf, matchers.Font)
}
// Match file as generic archive type
func Archive(buf []byte) (types.Type, error) {
return doMatchMap(buf, matchers.Archive)
}
func Is(buf []byte, ext string) bool {
kind, ok := types.Types[ext]
if ok {
return IsType(buf, kind)
}
return false
}
func IsType(buf []byte, kind types.Type) bool {
matcher := matchers.Matchers[kind]
if matcher == nil {
return false
}
length := len(buf)
return matcher(buf, length) != types.Unknown
}
// Register a new matcher type
func AddMatcher(fileType types.Type, matcher matchers.Matcher) matchers.TypeMatcher {
return matchers.NewMatcher(fileType, matcher)
}
// Register a new file type
func AddType(ext, mime string) types.Type {
return types.NewType(ext, mime)
}
// Check if a given file extension is supported
func IsSupported(ext string) bool {
for name, _ := range Types {
if name == ext {
return true
}
}
return false
}
// Check if a given MIME expression is supported
func IsMIMESupported(mime string) bool {
for _, m := range Types {
if m.MIME.Value == mime {
return true
}
}
return false
}

25
filetype_test.go Normal file
View File

@@ -0,0 +1,25 @@
package filetype
import (
"testing"
)
func TestMatches(t *testing.T) {
cases := []struct {
buf []byte
ext string
}{
{[]byte{0xFF, 0xD8, 0xFF}, "jpg"},
}
for _, test := range cases {
match, err := Match(test.buf)
if err != nil {
t.Fatalf("Error: %s", err)
}
if match.Extension != test.ext {
t.Fatalf("Invalid image type: %s", match.Extension)
}
}
}

134
matchers/archive.go Normal file
View File

@@ -0,0 +1,134 @@
package matchers
var TypeEpub = NewType("epub", "application/epub+zip")
var TypeZip = NewType("zip", "application/zip")
var TypeTar = NewType("tar", "application/x-tar")
var TypeRar = NewType("rar", "application/x-rar-compressed")
var TypeGz = NewType("gz", "application/gzip")
var TypeBz2 = NewType("bz2", "application/x-bzip2")
var Type7z = NewType("7z", "application/x-7z-compressed")
var TypeXz = NewType("xz", "application/x-xz")
var TypePdf = NewType("pdf", "application/pdf")
var TypeExe = NewType("exe", "application/x-msdownload")
var TypeSwf = NewType("swf", "application/x-shockwave-flash")
var TypeRtf = NewType("rtf", "application/rtf")
var TypeEot = NewType("eot", "application/octet-stream")
var TypePs = NewType("ps", "application/postscript")
var TypeSqlite = NewType("sqlite", "application/x-sqlite3")
var Archive = Map{
TypeEpub: Epub,
TypeZip: Zip,
TypeTar: Tar,
TypeRar: Rar,
TypeBz2: Bz2,
Type7z: SevenZ,
TypeXz: Xz,
TypePdf: Pdf,
TypeExe: Exe,
TypeSwf: Swf,
TypeRtf: Rtf,
TypeEot: Eot,
TypePs: Ps,
TypeSqlite: Sqlite,
}
func Epub(buf []byte, length int) bool {
return length > 57 &&
buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x3 && buf[3] == 0x4 &&
buf[30] == 0x6D && buf[31] == 0x69 && buf[32] == 0x6D && buf[33] == 0x65 &&
buf[34] == 0x74 && buf[35] == 0x79 && buf[36] == 0x70 && buf[37] == 0x65 &&
buf[38] == 0x61 && buf[39] == 0x70 && buf[40] == 0x70 && buf[41] == 0x6C &&
buf[42] == 0x69 && buf[43] == 0x63 && buf[44] == 0x61 && buf[45] == 0x74 &&
buf[46] == 0x69 && buf[47] == 0x6F && buf[48] == 0x6E && buf[49] == 0x2F &&
buf[50] == 0x65 && buf[51] == 0x70 && buf[52] == 0x75 && buf[53] == 0x62 &&
buf[54] == 0x2B && buf[55] == 0x7A && buf[56] == 0x69 && buf[57] == 0x70
}
func Zip(buf []byte, length int) bool {
return length > 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, length int) bool {
return length > 261 &&
buf[257] == 0x75 && buf[258] == 0x73 &&
buf[259] == 0x74 && buf[260] == 0x61 &&
buf[261] == 0x72
}
func Rar(buf []byte, length int) bool {
return length > 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 Gz(buf []byte, length int) bool {
return length > 2 &&
buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x8
}
func Bz2(buf []byte, length int) bool {
return length > 2 &&
buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68
}
func SevenZ(buf []byte, length int) bool {
return length > 5 &&
buf[0] == 0x37 && buf[1] == 0x7A && buf[2] == 0xBC &&
buf[3] == 0xAF && buf[4] == 0x27 && buf[5] == 0x1C
}
func Pdf(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x25 && buf[1] == 0x50 &&
buf[2] == 0x44 && buf[3] == 0x46
}
func Exe(buf []byte, length int) bool {
return length > 1 &&
buf[0] == 0x4D && buf[1] == 0x5A
}
func Swf(buf []byte, length int) bool {
return length > 2 &&
(buf[0] == 0x43 || buf[0] == 0x46) &&
buf[1] == 0x57 && buf[2] == 0x53
}
func Rtf(buf []byte, length int) bool {
return length > 4 &&
buf[0] == 0x7B && buf[1] == 0x5C &&
buf[2] == 0x72 && buf[3] == 0x74 &&
buf[4] == 0x66
}
func Eot(buf []byte, length int) bool {
return length > 10 &&
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 Ps(buf []byte, length int) bool {
return length > 1 &&
buf[0] == 0x25 && buf[1] == 0x21
}
func Xz(buf []byte, length int) bool {
return length > 5 &&
buf[0] == 0xFD && buf[1] == 0x37 &&
buf[2] == 0x7A && buf[3] == 0x58 &&
buf[4] == 0x5A && buf[5] == 0x00
}
func Sqlite(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x53 && buf[1] == 0x51 &&
buf[2] == 0x4C && buf[3] == 0x69
}

54
matchers/audio.go Normal file
View File

@@ -0,0 +1,54 @@
package matchers
var TypeMidi = NewType("mid", "audio/midi")
var TypeMp3 = NewType("mp3", "audio/mpeg")
var TypeM4a = NewType("m4a", "audio/m4a")
var TypeOgg = NewType("ogg", "audio/ogg")
var TypeFlac = NewType("flac", "audio/x-flac")
var TypeWav = NewType("wav", "audio/x-wav")
var Audio = Map{
TypeMidi: Midi,
TypeMp3: Mp3,
TypeM4a: M4a,
TypeOgg: Ogg,
TypeFlac: Flac,
TypeWav: Wav,
}
func Midi(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x4D && buf[1] == 0x54 &&
buf[2] == 0x68 && buf[3] == 0x64
}
func Mp3(buf []byte, length int) bool {
return length > 2 &&
(buf[0] == 0x49 && buf[1] == 0x44 && buf[2] == 0x33) ||
(buf[0] == 0xFF && buf[1] == 0xfb)
}
func M4a(buf []byte, length int) bool {
return length > 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, length int) bool {
return length > 3 &&
buf[0] == 0x4F && buf[1] == 0x67 &&
buf[2] == 0x67 && buf[3] == 0x53
}
func Flac(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x66 && buf[1] == 0x4C &&
buf[2] == 0x61 && buf[3] == 0x43
}
func Wav(buf []byte, length int) bool {
return length > 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
}

39
matchers/fonts.go Normal file
View File

@@ -0,0 +1,39 @@
package matchers
var TypeWoff = NewType("woff", "application/font-woff")
var TypeWoff2 = NewType("woff2", "application/font-woff")
var TypeTtf = NewType("ttf", "application/font-sfnt")
var TypeOtf = NewType("otf", "application/font-sfnt")
var Font = Map{
TypeWoff: Woff,
TypeWoff2: Woff2,
TypeTtf: Ttf,
TypeOtf: Otf,
}
func Woff(buf []byte, length int) bool {
return length > 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, length int) bool {
return length > 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, length int) bool {
return length > 4 &&
buf[0] == 0x00 && buf[1] == 0x01 &&
buf[2] == 0x00 && buf[3] == 0x00 &&
buf[4] == 0x00
}
func Otf(buf []byte, length int) bool {
return length > 4 &&
buf[0] == 0x4F && buf[1] == 0x54 &&
buf[2] == 0x54 && buf[3] == 0x4F &&
buf[4] == 0x00
}

82
matchers/image.go Normal file
View File

@@ -0,0 +1,82 @@
package matchers
var TypeJpeg = NewType("jpg", "image/jpeg")
var TypePng = NewType("png", "image/png")
var TypeGif = NewType("gif", "image/gif")
var TypeWebp = NewType("webp", "image/webp")
var TypeCR2 = NewType("cr2", "image/x-canon-cr2")
var TypeTiff = NewType("tif", "image/tiff")
var TypeBmp = NewType("bmp", "image/bmp")
var TypeJxr = NewType("jxr", "image/vnd.ms-photo")
var TypePsd = NewType("psd", "image/vnd.adobe.photoshop")
var TypeIco = NewType("ico", "image/x-icon")
var Image = Map{
TypeJpeg: Jpeg,
TypePng: Png,
TypeGif: Gif,
TypeWebp: Webp,
TypeCR2: CR2,
TypeTiff: Tiff,
TypeBmp: Bmp,
TypeJxr: Jxr,
TypePsd: Psd,
TypeIco: Ico,
}
func Jpeg(buf []byte, length int) bool {
return length > 2 &&
buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF
}
func Png(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x89 && buf[1] == 0x50 &&
buf[2] == 0x4E && buf[3] == 0x47
}
func Gif(buf []byte, length int) bool {
return length > 2 &&
buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46
}
func Webp(buf []byte, length int) bool {
return length > 11 &&
buf[8] == 0x57 && buf[9] == 0x45 &&
buf[10] == 0x42 && buf[11] == 0x50
}
func CR2(buf []byte, length int) bool {
return length > 9 &&
((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) &&
buf[8] == 0x43 && buf[9] == 0x52
}
func Tiff(buf []byte, length int) bool {
return length > 3 &&
(buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)
}
func Bmp(buf []byte, length int) bool {
return length > 1 &&
buf[0] == 0x42 && buf[1] == 0x4D
}
func Jxr(buf []byte, length int) bool {
return length > 2 &&
buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0xBC
}
func Psd(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x38 && buf[1] == 0x42 &&
buf[2] == 0x50 && buf[3] == 0x53
}
func Ico(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x00 && buf[1] == 0x00 &&
buf[2] == 0x01 && buf[3] == 0x00
}

80
matchers/matcher.go Normal file
View File

@@ -0,0 +1,80 @@
package matchers
import "gopkg.in/h2non/filetype.v0/types"
type Map map[types.Type]Matcher
// Matcher function interface as type alias
type Matcher func([]byte, int) bool
// Type specific matcher function interface
type TypeMatcher func([]byte, int) types.Type
// Store registered file type matchers
var Matchers = make(map[types.Type]TypeMatcher)
var NewType = types.NewType
// Create and register a new type matcher function
func NewMatcher(kind types.Type, fn Matcher) TypeMatcher {
matcher := func(buf []byte, length int) types.Type {
if fn(buf, length) {
return kind
}
return types.Unknown
}
Matchers[kind] = matcher
return matcher
}
// Match the file type of a given buffer
func Match(buf []byte) (types.Type, error) {
length := len(buf)
if length == 0 {
return types.Empty, nil
}
for _, checker := range Matchers {
match := checker(buf, length)
if match != types.Unknown && match.Extension != "" {
return match, nil
}
}
return types.Unknown, nil
}
func MatchType(buf []byte, kind types.Type) bool {
return true
}
func MatchMap(buf []byte, matchers Map) types.Type {
length := len(buf)
for kind, matcher := range matchers {
if matcher(buf, length) {
return kind
}
}
return types.Unknown
}
func MatchesMap(buf []byte, matchers Map) bool {
return MatchMap(buf, matchers) != types.Unknown
}
func Matches(buf []byte) bool {
kind, _ := Match(buf)
return kind != types.Unknown
}
func register(matchers ...Map) {
for _, m := range matchers {
for kind, matcher := range m {
NewMatcher(kind, matcher)
}
}
}
func init() {
register(Image, Video, Audio, Font, Archive)
}

97
matchers/video.go Normal file
View File

@@ -0,0 +1,97 @@
package matchers
var TypeMp4 = NewType("mp4", "video/mp4")
var TypeM4v = NewType("m4v", "video/x-m4v")
var TypeMkv = NewType("mkv", "video/x-matroska")
var TypeWebm = NewType("webm", "video/webm")
var TypeMov = NewType("mov", "video/quicktime")
var TypeAvi = NewType("avi", "video/x-msvideo")
var TypeWmv = NewType("wmv", "video/x-ms-wmv")
var TypeMpeg = NewType("mpg", "video/mpeg")
var TypeFlv = NewType("flv", "video/x-flv")
var Video = Map{
TypeMp4: Mp4,
TypeM4v: M4v,
TypeMkv: Mkv,
TypeMov: Mov,
TypeAvi: Avi,
TypeWmv: Wmv,
TypeMpeg: Mpeg,
TypeFlv: Flv,
}
func M4v(buf []byte, length int) bool {
return length > 10 &&
buf[0] == 0x0 && buf[1] == 0x0 &&
buf[2] == 0x0 && buf[3] == 0x1C &&
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, length int) bool {
return length > 38 &&
buf[31] == 0x6D && buf[32] == 0x61 &&
buf[33] == 0x74 && buf[34] == 0x72 &&
buf[35] == 0x6f && buf[36] == 0x73 &&
buf[37] == 0x6B && buf[38] == 0x61
}
func Webm(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x1A && buf[1] == 0x45 &&
buf[2] == 0xDF && buf[3] == 0xA3
}
func Mov(buf []byte, length int) bool {
return length > 7 &&
buf[0] == 0x0 && buf[1] == 0x0 &&
buf[2] == 0x0 && buf[3] == 0x14 &&
buf[4] == 0x66 && buf[5] == 0x74 &&
buf[6] == 0x79 && buf[7] == 0x70
}
func Avi(buf []byte, length int) bool {
return length > 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, length int) bool {
return length > 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, length int) bool {
return length > 3 &&
buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x1 &&
buf[3] >= 0xb0 && buf[3] <= 0xbf
}
func Flv(buf []byte, length int) bool {
return length > 3 &&
buf[0] == 0x46 && buf[1] == 0x4C &&
buf[2] == 0x56 && buf[3] == 0x01
}
func Mp4(buf []byte, length int) bool {
return length > 27 &&
(buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 &&
(buf[3] == 0x18 || buf[3] == 0x20) && buf[4] == 0x66 &&
buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70) ||
(buf[0] == 0x33 && buf[1] == 0x67 && buf[2] == 0x70 && buf[3] == 0x35) ||
(buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x1C &&
buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 &&
buf[8] == 0x6D && buf[9] == 0x70 && buf[10] == 0x34 && buf[11] == 0x32 &&
buf[16] == 0x6D && buf[17] == 0x70 && buf[18] == 0x34 && buf[19] == 0x31 &&
buf[20] == 0x6D && buf[21] == 0x70 && buf[22] == 0x34 && buf[23] == 0x32 &&
buf[24] == 0x69 && buf[25] == 0x73 && buf[26] == 0x6F && buf[27] == 0x6D)
}

4
types/defaults.go Normal file
View File

@@ -0,0 +1,4 @@
package types
var Empty = NewType("", "")
var Unknown = NewType("unknown", "")

12
types/mime.go Normal file
View File

@@ -0,0 +1,12 @@
package types
type MIME struct {
Type string
Subtype string
Value string
}
func NewMIME(mime string) MIME {
kind, subtype := splitMime(mime)
return MIME{Type: kind, Subtype: subtype, Value: mime}
}

11
types/split.go Normal file
View File

@@ -0,0 +1,11 @@
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], ""
}

14
types/type.go Normal file
View File

@@ -0,0 +1,14 @@
package types
type Type struct {
MIME MIME
Extension string
}
func NewType(ext, mime string) Type {
t := Type{
MIME: NewMIME(mime),
Extension: ext,
}
return Add(t)
}

8
types/types.go Normal file
View File

@@ -0,0 +1,8 @@
package types
var Types = make(map[string]Type)
func Add(t Type) Type {
Types[t.Extension] = t
return t
}