├── go.mod
├── README.md
├── LICENSE
└── magic
├── mime.go
└── magic.go
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/vimeo/go-magic
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-magic #
2 |
3 | ## Go library for getting MIME type using libmagic ##
4 |
5 | ### Installing ###
6 |
7 | ```
8 | go get github.com/vimeo/go-magic/magic
9 | ```
10 |
11 | ### Dependencies ###
12 |
13 | **libmagic**
14 | *URL*: [http://www.darwinsys.com/file/](http://www.darwinsys.com/file/)
15 | *Ubuntu*: `apt-get install libmagic-dev`
16 | *CentOS*: `yum install file-devel`
17 |
18 | ### Usage ###
19 |
20 | - Create some custom magic files (e.g. ~/magicfiles)
21 | - Add the default system magic file dir
22 | - magic.AddMagicDir(magic.GetDefaultDir())
23 | - Add the custom magic file dir
24 | - magic.AddMagicDir("~/magicfiles")
25 | - Get MIME type with either one of:
26 | - magic.MimeFromFile(filename)
27 | - magic.MimeFromBytes(data)
28 |
29 | ### API Documentation ###
30 |
31 | [http://godoc.org/github.com/vimeo/go-magic/magic](http://godoc.org/github.com/vimeo/go-magic/magic)
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, Vimeo, LLC.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
24 | The views and conclusions contained in the software and documentation are those
25 | of the authors and should not be interpreted as representing official policies,
26 | either expressed or implied, of Vimeo, LLC.
27 |
--------------------------------------------------------------------------------
/magic/mime.go:
--------------------------------------------------------------------------------
1 | package magic
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "os"
7 | "path/filepath"
8 | "strings"
9 | "sync"
10 | )
11 |
12 | var magicFiles map[string]bool
13 | var mutex sync.Mutex
14 |
15 | func init() {
16 | magicFiles = make(map[string]bool)
17 | }
18 |
19 | func compileToMgc(file string) {
20 | cookie := Open(MAGIC_NONE)
21 | defer Close(cookie)
22 | Compile(cookie, file)
23 | }
24 |
25 | func compileMagicFiles(dir string, files []string) {
26 | // for some reason libmagic puts compiled files in the current working dir
27 | // instead of the dir of the source file, so switch to the source dir,
28 | // compile, then switch back
29 | pwd, err := os.Getwd()
30 | if err != nil {
31 | return
32 | }
33 | err = os.Chdir(dir)
34 | if err != nil {
35 | return
36 | }
37 | defer os.Chdir(pwd)
38 |
39 | for _, f := range files {
40 | compileToMgc(f)
41 | }
42 | }
43 |
44 | /* Add a directory for libmagic to search for .mgc databases. */
45 | func AddMagicDir(dir string) error {
46 | var err error
47 |
48 | dir, err = filepath.Abs(dir)
49 | if err != nil {
50 | return err
51 | }
52 | fi, err := os.Stat(dir)
53 | if err != nil {
54 | return err
55 | }
56 | if fi.IsDir() == false {
57 | return fmt.Errorf("Not a directory: %s", dir)
58 | }
59 |
60 | // get list of .magic files that need to be compiled to .mgc
61 | var srcFiles []string
62 | files, err := ioutil.ReadDir(dir)
63 | if err != nil {
64 | return err
65 | }
66 | for _, fi = range files {
67 | if filepath.Ext(fi.Name()) == ".magic" {
68 | mgcSrc := filepath.Join(dir, fi.Name())
69 | _, err := os.Stat(mgcSrc + ".mgc")
70 | if err != nil {
71 | srcFiles = append(srcFiles, mgcSrc)
72 | }
73 | }
74 | }
75 | // compile .magic files
76 | if len(srcFiles) > 0 {
77 | compileMagicFiles(dir, srcFiles)
78 | }
79 |
80 | files, err = ioutil.ReadDir(dir)
81 | if err != nil {
82 | return err
83 | }
84 | mutex.Lock()
85 | for _, fi = range files {
86 | if filepath.Ext(fi.Name()) == ".mgc" {
87 | mgcFile := filepath.Join(dir, fi.Name())
88 | magicFiles[mgcFile] = true
89 | }
90 | }
91 | mutex.Unlock()
92 |
93 | return nil
94 | }
95 |
96 | /* Get mimetype from a file. */
97 | func MimeFromFile(path string) string {
98 | cookie := Open(MAGIC_ERROR | MAGIC_MIME_TYPE)
99 | defer Close(cookie)
100 | mutex.Lock()
101 | var mf []string
102 | for f := range magicFiles {
103 | mf = append(mf, f)
104 | }
105 | mutex.Unlock()
106 | ret := Load(cookie, strings.Join(mf, ":"))
107 | if ret != 0 {
108 | return "application/octet-stream"
109 | }
110 | r := File(cookie, path)
111 | return r
112 | }
113 |
114 | /* Get mimetype from a buffer. */
115 | func MimeFromBytes(b []byte) string {
116 | cookie := Open(MAGIC_ERROR | MAGIC_MIME_TYPE)
117 | defer Close(cookie)
118 | mutex.Lock()
119 | var mf []string
120 | for f := range magicFiles {
121 | mf = append(mf, f)
122 | }
123 | mutex.Unlock()
124 | ret := Load(cookie, strings.Join(mf, ":"))
125 | if ret != 0 {
126 | return "application/octet-stream"
127 | }
128 | r := Buffer(cookie, b)
129 | return r
130 | }
131 |
--------------------------------------------------------------------------------
/magic/magic.go:
--------------------------------------------------------------------------------
1 | package magic
2 |
3 | /*
4 | #cgo LDFLAGS: -lmagic
5 | #include
6 | #include
7 | */
8 | import "C"
9 | import (
10 | "os"
11 | "path"
12 | "unsafe"
13 | )
14 |
15 | const (
16 | MAGIC_NONE = C.MAGIC_NONE
17 | MAGIC_DEBUG = C.MAGIC_DEBUG
18 | MAGIC_SYMLINK = C.MAGIC_SYMLINK
19 | MAGIC_COMPRESS = C.MAGIC_COMPRESS
20 | MAGIC_DEVICES = C.MAGIC_DEVICES
21 | MAGIC_MIME_TYPE = C.MAGIC_MIME_TYPE
22 | MAGIC_CONTINUE = C.MAGIC_CONTINUE
23 | MAGIC_CHECK = C.MAGIC_CHECK
24 | MAGIC_PRESERVE_ATIME = C.MAGIC_PRESERVE_ATIME
25 | MAGIC_RAW = C.MAGIC_RAW
26 | MAGIC_ERROR = C.MAGIC_ERROR
27 | MAGIC_MIME_ENCODING = C.MAGIC_MIME_ENCODING
28 | MAGIC_MIME = C.MAGIC_MIME
29 | MAGIC_APPLE = C.MAGIC_APPLE
30 | MAGIC_NO_CHECK_COMPRESS = C.MAGIC_NO_CHECK_COMPRESS
31 | MAGIC_NO_CHECK_TAR = C.MAGIC_NO_CHECK_TAR
32 | MAGIC_NO_CHECK_SOFT = C.MAGIC_NO_CHECK_SOFT
33 | MAGIC_NO_CHECK_APPTYPE = C.MAGIC_NO_CHECK_APPTYPE
34 | MAGIC_NO_CHECK_ELF = C.MAGIC_NO_CHECK_ELF
35 | MAGIC_NO_CHECK_TEXT = C.MAGIC_NO_CHECK_TEXT
36 | MAGIC_NO_CHECK_CDF = C.MAGIC_NO_CHECK_CDF
37 | MAGIC_NO_CHECK_TOKENS = C.MAGIC_NO_CHECK_TOKENS
38 | MAGIC_NO_CHECK_ENCODING = C.MAGIC_NO_CHECK_ENCODING
39 | MAGIC_NO_CHECK_ASCII = C.MAGIC_NO_CHECK_ASCII
40 | MAGIC_NO_CHECK_FORTRAN = C.MAGIC_NO_CHECK_FORTRAN
41 | MAGIC_NO_CHECK_TROFF = C.MAGIC_NO_CHECK_TROFF
42 | )
43 | const (
44 | MAGIC_NO_CHECK_BUILTIN = MAGIC_NO_CHECK_COMPRESS |
45 | MAGIC_NO_CHECK_TAR |
46 | MAGIC_NO_CHECK_APPTYPE |
47 | MAGIC_NO_CHECK_ELF |
48 | MAGIC_NO_CHECK_TEXT |
49 | MAGIC_NO_CHECK_CDF |
50 | MAGIC_NO_CHECK_TOKENS |
51 | MAGIC_NO_CHECK_ENCODING
52 | )
53 |
54 | type Magic C.magic_t
55 |
56 | var system_mgc_locations = []string{
57 | "/usr/share/misc/magic.mgc",
58 | "/usr/share/file/magic.mgc",
59 | "/usr/share/magic/magic.mgc",
60 | }
61 |
62 | /* Find the real magic file location */
63 | func GetDefaultDir() string {
64 | var f string
65 |
66 | found_mgc := false
67 | for _, f = range system_mgc_locations {
68 | fi, err := os.Lstat(f)
69 | if err == nil && fi.Mode()&os.ModeSymlink != os.ModeSymlink {
70 | found_mgc = true
71 | break
72 | }
73 | }
74 | if found_mgc {
75 | return path.Dir(f)
76 | } else {
77 | return ""
78 | }
79 | }
80 |
81 | func Open(flags int) Magic {
82 | cookie := (Magic)(C.magic_open(C.int(flags)))
83 | return cookie
84 | }
85 |
86 | func Close(cookie Magic) {
87 | C.magic_close((C.magic_t)(cookie))
88 | }
89 |
90 | func Error(cookie Magic) string {
91 | s := (C.magic_error((C.magic_t)(cookie)))
92 | return C.GoString(s)
93 | }
94 |
95 | func Errno(cookie Magic) int {
96 | return (int)(C.magic_errno((C.magic_t)(cookie)))
97 | }
98 |
99 | func File(cookie Magic, filename string) string {
100 | cfilename := C.CString(filename)
101 | defer C.free(unsafe.Pointer(cfilename))
102 | return C.GoString(C.magic_file((C.magic_t)(cookie), cfilename))
103 | }
104 |
105 | func Buffer(cookie Magic, b []byte) string {
106 | length := C.size_t(len(b))
107 | return C.GoString(C.magic_buffer((C.magic_t)(cookie), unsafe.Pointer(&b[0]), length))
108 | }
109 |
110 | func SetFlags(cookie Magic, flags int) int {
111 | return (int)(C.magic_setflags((C.magic_t)(cookie), C.int(flags)))
112 | }
113 |
114 | func Check(cookie Magic, filename string) int {
115 | cfilename := C.CString(filename)
116 | defer C.free(unsafe.Pointer(cfilename))
117 | return (int)(C.magic_check((C.magic_t)(cookie), cfilename))
118 | }
119 |
120 | func Compile(cookie Magic, filename string) int {
121 | cfilename := C.CString(filename)
122 | defer C.free(unsafe.Pointer(cfilename))
123 | return (int)(C.magic_compile((C.magic_t)(cookie), cfilename))
124 | }
125 |
126 | func Load(cookie Magic, filename string) int {
127 | if filename == "" {
128 | return (int)(C.magic_load((C.magic_t)(cookie), nil))
129 | }
130 | cfilename := C.CString(filename)
131 | defer C.free(unsafe.Pointer(cfilename))
132 | return (int)(C.magic_load((C.magic_t)(cookie), cfilename))
133 | }
134 |
--------------------------------------------------------------------------------