├── 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 | --------------------------------------------------------------------------------