| @@ -0,0 +1,390 @@ | |||
| # coding: utf-8 | |||
| # from: https://github.com/file/file/raw/master/python/magic.py | |||
| # LICENSE: | |||
| # Copyright (c) Ian F. Darwin 1986-1995. | |||
| # Software written by Ian F. Darwin and others; | |||
| # maintained 1995-present by Christos Zoulas and others. | |||
| # | |||
| # Redistribution and use in source and binary forms, with or without | |||
| # modification, are permitted provided that the following conditions | |||
| # are met: | |||
| # 1. Redistributions of source code must retain the above copyright | |||
| # notice immediately at the beginning of the file, without modification, | |||
| # this list of conditions, and the following disclaimer. | |||
| # 2. 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. | |||
| # | |||
| # THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. | |||
| ''' | |||
| Python bindings for libmagic | |||
| ''' | |||
| import ctypes | |||
| import threading | |||
| from collections import namedtuple | |||
| from ctypes import * | |||
| from ctypes.util import find_library | |||
| def _init(): | |||
| """ | |||
| Loads the shared library through ctypes and returns a library | |||
| L{ctypes.CDLL} instance | |||
| """ | |||
| return ctypes.cdll.LoadLibrary(find_library('magic')) | |||
| _libraries = {} | |||
| _libraries['magic'] = _init() | |||
| # Flag constants for open and setflags | |||
| MAGIC_NONE = NONE = 0 | |||
| MAGIC_DEBUG = DEBUG = 1 | |||
| MAGIC_SYMLINK = SYMLINK = 2 | |||
| MAGIC_COMPRESS = COMPRESS = 4 | |||
| MAGIC_DEVICES = DEVICES = 8 | |||
| MAGIC_MIME_TYPE = MIME_TYPE = 16 | |||
| MAGIC_CONTINUE = CONTINUE = 32 | |||
| MAGIC_CHECK = CHECK = 64 | |||
| MAGIC_PRESERVE_ATIME = PRESERVE_ATIME = 128 | |||
| MAGIC_RAW = RAW = 256 | |||
| MAGIC_ERROR = ERROR = 512 | |||
| MAGIC_MIME_ENCODING = MIME_ENCODING = 1024 | |||
| MAGIC_MIME = MIME = 1040 # MIME_TYPE + MIME_ENCODING | |||
| MAGIC_APPLE = APPLE = 2048 | |||
| MAGIC_NO_CHECK_COMPRESS = NO_CHECK_COMPRESS = 4096 | |||
| MAGIC_NO_CHECK_TAR = NO_CHECK_TAR = 8192 | |||
| MAGIC_NO_CHECK_SOFT = NO_CHECK_SOFT = 16384 | |||
| MAGIC_NO_CHECK_APPTYPE = NO_CHECK_APPTYPE = 32768 | |||
| MAGIC_NO_CHECK_ELF = NO_CHECK_ELF = 65536 | |||
| MAGIC_NO_CHECK_TEXT = NO_CHECK_TEXT = 131072 | |||
| MAGIC_NO_CHECK_CDF = NO_CHECK_CDF = 262144 | |||
| MAGIC_NO_CHECK_TOKENS = NO_CHECK_TOKENS = 1048576 | |||
| MAGIC_NO_CHECK_ENCODING = NO_CHECK_ENCODING = 2097152 | |||
| MAGIC_NO_CHECK_BUILTIN = NO_CHECK_BUILTIN = 4173824 | |||
| MAGIC_PARAM_INDIR_MAX = PARAM_INDIR_MAX = 0 | |||
| MAGIC_PARAM_NAME_MAX = PARAM_NAME_MAX = 1 | |||
| MAGIC_PARAM_ELF_PHNUM_MAX = PARAM_ELF_PHNUM_MAX = 2 | |||
| MAGIC_PARAM_ELF_SHNUM_MAX = PARAM_ELF_SHNUM_MAX = 3 | |||
| MAGIC_PARAM_ELF_NOTES_MAX = PARAM_ELF_NOTES_MAX = 4 | |||
| MAGIC_PARAM_REGEX_MAX = PARAM_REGEX_MAX = 5 | |||
| MAGIC_PARAM_BYTES_MAX = PARAM_BYTES_MAX = 6 | |||
| FileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name')) | |||
| class magic_set(Structure): | |||
| pass | |||
| magic_set._fields_ = [] | |||
| magic_t = POINTER(magic_set) | |||
| _open = _libraries['magic'].magic_open | |||
| _open.restype = magic_t | |||
| _open.argtypes = [c_int] | |||
| _close = _libraries['magic'].magic_close | |||
| _close.restype = None | |||
| _close.argtypes = [magic_t] | |||
| _file = _libraries['magic'].magic_file | |||
| _file.restype = c_char_p | |||
| _file.argtypes = [magic_t, c_char_p] | |||
| _descriptor = _libraries['magic'].magic_descriptor | |||
| _descriptor.restype = c_char_p | |||
| _descriptor.argtypes = [magic_t, c_int] | |||
| _buffer = _libraries['magic'].magic_buffer | |||
| _buffer.restype = c_char_p | |||
| _buffer.argtypes = [magic_t, c_void_p, c_size_t] | |||
| _error = _libraries['magic'].magic_error | |||
| _error.restype = c_char_p | |||
| _error.argtypes = [magic_t] | |||
| _setflags = _libraries['magic'].magic_setflags | |||
| _setflags.restype = c_int | |||
| _setflags.argtypes = [magic_t, c_int] | |||
| _load = _libraries['magic'].magic_load | |||
| _load.restype = c_int | |||
| _load.argtypes = [magic_t, c_char_p] | |||
| _compile = _libraries['magic'].magic_compile | |||
| _compile.restype = c_int | |||
| _compile.argtypes = [magic_t, c_char_p] | |||
| _check = _libraries['magic'].magic_check | |||
| _check.restype = c_int | |||
| _check.argtypes = [magic_t, c_char_p] | |||
| _list = _libraries['magic'].magic_list | |||
| _list.restype = c_int | |||
| _list.argtypes = [magic_t, c_char_p] | |||
| _errno = _libraries['magic'].magic_errno | |||
| _errno.restype = c_int | |||
| _errno.argtypes = [magic_t] | |||
| _getparam = _libraries['magic'].magic_getparam | |||
| _getparam.restype = c_int | |||
| _getparam.argtypes = [magic_t, c_int, c_void_p] | |||
| _setparam = _libraries['magic'].magic_setparam | |||
| _setparam.restype = c_int | |||
| _setparam.argtypes = [magic_t, c_int, c_void_p] | |||
| class Magic(object): | |||
| def __init__(self, ms): | |||
| self._magic_t = ms | |||
| def close(self): | |||
| """ | |||
| Closes the magic database and deallocates any resources used. | |||
| """ | |||
| _close(self._magic_t) | |||
| @staticmethod | |||
| def __tostr(s): | |||
| if s is None: | |||
| return None | |||
| if isinstance(s, str): | |||
| return s | |||
| try: # keep Python 2 compatibility | |||
| return str(s, 'utf-8') | |||
| except TypeError: | |||
| return str(s) | |||
| @staticmethod | |||
| def __tobytes(b): | |||
| if b is None: | |||
| return None | |||
| if isinstance(b, bytes): | |||
| return b | |||
| try: # keep Python 2 compatibility | |||
| return bytes(b, 'utf-8') | |||
| except TypeError: | |||
| return bytes(b) | |||
| def file(self, filename): | |||
| """ | |||
| Returns a textual description of the contents of the argument passed | |||
| as a filename or None if an error occurred and the MAGIC_ERROR flag | |||
| is set. A call to errno() will return the numeric error code. | |||
| """ | |||
| return Magic.__tostr(_file(self._magic_t, Magic.__tobytes(filename))) | |||
| def descriptor(self, fd): | |||
| """ | |||
| Returns a textual description of the contents of the argument passed | |||
| as a file descriptor or None if an error occurred and the MAGIC_ERROR | |||
| flag is set. A call to errno() will return the numeric error code. | |||
| """ | |||
| return Magic.__tostr(_descriptor(self._magic_t, fd)) | |||
| def buffer(self, buf): | |||
| """ | |||
| Returns a textual description of the contents of the argument passed | |||
| as a buffer or None if an error occurred and the MAGIC_ERROR flag | |||
| is set. A call to errno() will return the numeric error code. | |||
| """ | |||
| return Magic.__tostr(_buffer(self._magic_t, buf, len(buf))) | |||
| def error(self): | |||
| """ | |||
| Returns a textual explanation of the last error or None | |||
| if there was no error. | |||
| """ | |||
| return Magic.__tostr(_error(self._magic_t)) | |||
| def setflags(self, flags): | |||
| """ | |||
| Set flags on the magic object which determine how magic checking | |||
| behaves; a bitwise OR of the flags described in libmagic(3), but | |||
| without the MAGIC_ prefix. | |||
| Returns -1 on systems that don't support utime(2) or utimes(2) | |||
| when PRESERVE_ATIME is set. | |||
| """ | |||
| return _setflags(self._magic_t, flags) | |||
| def load(self, filename=None): | |||
| """ | |||
| Must be called to load entries in the colon separated list of database | |||
| files passed as argument or the default database file if no argument | |||
| before any magic queries can be performed. | |||
| Returns 0 on success and -1 on failure. | |||
| """ | |||
| return _load(self._magic_t, Magic.__tobytes(filename)) | |||
| def compile(self, dbs): | |||
| """ | |||
| Compile entries in the colon separated list of database files | |||
| passed as argument or the default database file if no argument. | |||
| The compiled files created are named from the basename(1) of each file | |||
| argument with ".mgc" appended to it. | |||
| Returns 0 on success and -1 on failure. | |||
| """ | |||
| return _compile(self._magic_t, Magic.__tobytes(dbs)) | |||
| def check(self, dbs): | |||
| """ | |||
| Check the validity of entries in the colon separated list of | |||
| database files passed as argument or the default database file | |||
| if no argument. | |||
| Returns 0 on success and -1 on failure. | |||
| """ | |||
| return _check(self._magic_t, Magic.__tobytes(dbs)) | |||
| def list(self, dbs): | |||
| """ | |||
| Check the validity of entries in the colon separated list of | |||
| database files passed as argument or the default database file | |||
| if no argument. | |||
| Returns 0 on success and -1 on failure. | |||
| """ | |||
| return _list(self._magic_t, Magic.__tobytes(dbs)) | |||
| def errno(self): | |||
| """ | |||
| Returns a numeric error code. If return value is 0, an internal | |||
| magic error occurred. If return value is non-zero, the value is | |||
| an OS error code. Use the errno module or os.strerror() can be used | |||
| to provide detailed error information. | |||
| """ | |||
| return _errno(self._magic_t) | |||
| def getparam(self, param): | |||
| """ | |||
| Returns the param value if successful and -1 if the parameter | |||
| was unknown. | |||
| """ | |||
| v = c_int() | |||
| i = _getparam(self._magic_t, param, byref(v)) | |||
| if i == -1: | |||
| return -1 | |||
| return v.value | |||
| def setparam(self, param, value): | |||
| """ | |||
| Returns 0 if successful and -1 if the parameter was unknown. | |||
| """ | |||
| v = c_int(value) | |||
| return _setparam(self._magic_t, param, byref(v)) | |||
| def open(flags): | |||
| """ | |||
| Returns a magic object on success and None on failure. | |||
| Flags argument as for setflags. | |||
| """ | |||
| magic_t = _open(flags) | |||
| if magic_t is None: | |||
| return None | |||
| return Magic(magic_t) | |||
| # Objects used by `detect_from_` functions | |||
| class error(Exception): | |||
| pass | |||
| class MagicDetect(object): | |||
| def __init__(self): | |||
| self.mime_magic = open(MAGIC_MIME) | |||
| if self.mime_magic is None: | |||
| raise error | |||
| if self.mime_magic.load() == -1: | |||
| self.mime_magic.close() | |||
| self.mime_magic = None | |||
| raise error | |||
| self.none_magic = open(MAGIC_NONE) | |||
| if self.none_magic is None: | |||
| self.mime_magic.close() | |||
| self.mime_magic = None | |||
| raise error | |||
| if self.none_magic.load() == -1: | |||
| self.none_magic.close() | |||
| self.none_magic = None | |||
| self.mime_magic.close() | |||
| self.mime_magic = None | |||
| raise error | |||
| def __del__(self): | |||
| if self.mime_magic is not None: | |||
| self.mime_magic.close() | |||
| if self.none_magic is not None: | |||
| self.none_magic.close() | |||
| threadlocal = threading.local() | |||
| def _detect_make(): | |||
| v = getattr(threadlocal, "magic_instance", None) | |||
| if v is None: | |||
| v = MagicDetect() | |||
| setattr(threadlocal, "magic_instance", v) | |||
| return v | |||
| def _create_filemagic(mime_detected, type_detected): | |||
| try: | |||
| mime_type, mime_encoding = mime_detected.split('; ') | |||
| except ValueError: | |||
| raise ValueError(mime_detected) | |||
| return FileMagic(name=type_detected, mime_type=mime_type, | |||
| encoding=mime_encoding.replace('charset=', '')) | |||
| def detect_from_filename(filename): | |||
| '''Detect mime type, encoding and file type from a filename | |||
| Returns a `FileMagic` namedtuple. | |||
| ''' | |||
| x = _detect_make() | |||
| return _create_filemagic(x.mime_magic.file(filename), | |||
| x.none_magic.file(filename)) | |||
| def detect_from_fobj(fobj): | |||
| '''Detect mime type, encoding and file type from file-like object | |||
| Returns a `FileMagic` namedtuple. | |||
| ''' | |||
| file_descriptor = fobj.fileno() | |||
| x = _detect_make() | |||
| return _create_filemagic(x.mime_magic.descriptor(file_descriptor), | |||
| x.none_magic.descriptor(file_descriptor)) | |||
| def detect_from_content(byte_content): | |||
| '''Detect mime type, encoding and file type from bytes | |||
| Returns a `FileMagic` namedtuple. | |||
| ''' | |||
| x = _detect_make() | |||
| return _create_filemagic(x.mime_magic.buffer(byte_content), | |||
| x.none_magic.buffer(byte_content)) | |||