compat.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. # coding: utf-8
  2. '''
  3. Python bindings for libmagic
  4. '''
  5. import ctypes
  6. from collections import namedtuple
  7. from ctypes import *
  8. from ctypes.util import find_library
  9. from . import loader
  10. _libraries = {}
  11. _libraries['magic'] = loader.load_lib()
  12. # Flag constants for open and setflags
  13. MAGIC_NONE = NONE = 0
  14. MAGIC_DEBUG = DEBUG = 1
  15. MAGIC_SYMLINK = SYMLINK = 2
  16. MAGIC_COMPRESS = COMPRESS = 4
  17. MAGIC_DEVICES = DEVICES = 8
  18. MAGIC_MIME_TYPE = MIME_TYPE = 16
  19. MAGIC_CONTINUE = CONTINUE = 32
  20. MAGIC_CHECK = CHECK = 64
  21. MAGIC_PRESERVE_ATIME = PRESERVE_ATIME = 128
  22. MAGIC_RAW = RAW = 256
  23. MAGIC_ERROR = ERROR = 512
  24. MAGIC_MIME_ENCODING = MIME_ENCODING = 1024
  25. MAGIC_MIME = MIME = 1040 # MIME_TYPE + MIME_ENCODING
  26. MAGIC_APPLE = APPLE = 2048
  27. MAGIC_NO_CHECK_COMPRESS = NO_CHECK_COMPRESS = 4096
  28. MAGIC_NO_CHECK_TAR = NO_CHECK_TAR = 8192
  29. MAGIC_NO_CHECK_SOFT = NO_CHECK_SOFT = 16384
  30. MAGIC_NO_CHECK_APPTYPE = NO_CHECK_APPTYPE = 32768
  31. MAGIC_NO_CHECK_ELF = NO_CHECK_ELF = 65536
  32. MAGIC_NO_CHECK_TEXT = NO_CHECK_TEXT = 131072
  33. MAGIC_NO_CHECK_CDF = NO_CHECK_CDF = 262144
  34. MAGIC_NO_CHECK_TOKENS = NO_CHECK_TOKENS = 1048576
  35. MAGIC_NO_CHECK_ENCODING = NO_CHECK_ENCODING = 2097152
  36. MAGIC_NO_CHECK_BUILTIN = NO_CHECK_BUILTIN = 4173824
  37. FileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name'))
  38. class magic_set(Structure):
  39. pass
  40. magic_set._fields_ = []
  41. magic_t = POINTER(magic_set)
  42. _open = _libraries['magic'].magic_open
  43. _open.restype = magic_t
  44. _open.argtypes = [c_int]
  45. _close = _libraries['magic'].magic_close
  46. _close.restype = None
  47. _close.argtypes = [magic_t]
  48. _file = _libraries['magic'].magic_file
  49. _file.restype = c_char_p
  50. _file.argtypes = [magic_t, c_char_p]
  51. _descriptor = _libraries['magic'].magic_descriptor
  52. _descriptor.restype = c_char_p
  53. _descriptor.argtypes = [magic_t, c_int]
  54. _buffer = _libraries['magic'].magic_buffer
  55. _buffer.restype = c_char_p
  56. _buffer.argtypes = [magic_t, c_void_p, c_size_t]
  57. _error = _libraries['magic'].magic_error
  58. _error.restype = c_char_p
  59. _error.argtypes = [magic_t]
  60. _setflags = _libraries['magic'].magic_setflags
  61. _setflags.restype = c_int
  62. _setflags.argtypes = [magic_t, c_int]
  63. _load = _libraries['magic'].magic_load
  64. _load.restype = c_int
  65. _load.argtypes = [magic_t, c_char_p]
  66. _compile = _libraries['magic'].magic_compile
  67. _compile.restype = c_int
  68. _compile.argtypes = [magic_t, c_char_p]
  69. _check = _libraries['magic'].magic_check
  70. _check.restype = c_int
  71. _check.argtypes = [magic_t, c_char_p]
  72. _list = _libraries['magic'].magic_list
  73. _list.restype = c_int
  74. _list.argtypes = [magic_t, c_char_p]
  75. _errno = _libraries['magic'].magic_errno
  76. _errno.restype = c_int
  77. _errno.argtypes = [magic_t]
  78. class Magic(object):
  79. def __init__(self, ms):
  80. self._magic_t = ms
  81. def close(self):
  82. """
  83. Closes the magic database and deallocates any resources used.
  84. """
  85. _close(self._magic_t)
  86. @staticmethod
  87. def __tostr(s):
  88. if s is None:
  89. return None
  90. if isinstance(s, str):
  91. return s
  92. try: # keep Python 2 compatibility
  93. return str(s, 'utf-8')
  94. except TypeError:
  95. return str(s)
  96. @staticmethod
  97. def __tobytes(b):
  98. if b is None:
  99. return None
  100. if isinstance(b, bytes):
  101. return b
  102. try: # keep Python 2 compatibility
  103. return bytes(b, 'utf-8')
  104. except TypeError:
  105. return bytes(b)
  106. def file(self, filename):
  107. """
  108. Returns a textual description of the contents of the argument passed
  109. as a filename or None if an error occurred and the MAGIC_ERROR flag
  110. is set. A call to errno() will return the numeric error code.
  111. """
  112. return Magic.__tostr(_file(self._magic_t, Magic.__tobytes(filename)))
  113. def descriptor(self, fd):
  114. """
  115. Returns a textual description of the contents of the argument passed
  116. as a file descriptor or None if an error occurred and the MAGIC_ERROR
  117. flag is set. A call to errno() will return the numeric error code.
  118. """
  119. return Magic.__tostr(_descriptor(self._magic_t, fd))
  120. def buffer(self, buf):
  121. """
  122. Returns a textual description of the contents of the argument passed
  123. as a buffer or None if an error occurred and the MAGIC_ERROR flag
  124. is set. A call to errno() will return the numeric error code.
  125. """
  126. return Magic.__tostr(_buffer(self._magic_t, buf, len(buf)))
  127. def error(self):
  128. """
  129. Returns a textual explanation of the last error or None
  130. if there was no error.
  131. """
  132. return Magic.__tostr(_error(self._magic_t))
  133. def setflags(self, flags):
  134. """
  135. Set flags on the magic object which determine how magic checking
  136. behaves; a bitwise OR of the flags described in libmagic(3), but
  137. without the MAGIC_ prefix.
  138. Returns -1 on systems that don't support utime(2) or utimes(2)
  139. when PRESERVE_ATIME is set.
  140. """
  141. return _setflags(self._magic_t, flags)
  142. def load(self, filename=None):
  143. """
  144. Must be called to load entries in the colon separated list of database
  145. files passed as argument or the default database file if no argument
  146. before any magic queries can be performed.
  147. Returns 0 on success and -1 on failure.
  148. """
  149. return _load(self._magic_t, Magic.__tobytes(filename))
  150. def compile(self, dbs):
  151. """
  152. Compile entries in the colon separated list of database files
  153. passed as argument or the default database file if no argument.
  154. The compiled files created are named from the basename(1) of each file
  155. argument with ".mgc" appended to it.
  156. Returns 0 on success and -1 on failure.
  157. """
  158. return _compile(self._magic_t, Magic.__tobytes(dbs))
  159. def check(self, dbs):
  160. """
  161. Check the validity of entries in the colon separated list of
  162. database files passed as argument or the default database file
  163. if no argument.
  164. Returns 0 on success and -1 on failure.
  165. """
  166. return _check(self._magic_t, Magic.__tobytes(dbs))
  167. def list(self, dbs):
  168. """
  169. Check the validity of entries in the colon separated list of
  170. database files passed as argument or the default database file
  171. if no argument.
  172. Returns 0 on success and -1 on failure.
  173. """
  174. return _list(self._magic_t, Magic.__tobytes(dbs))
  175. def errno(self):
  176. """
  177. Returns a numeric error code. If return value is 0, an internal
  178. magic error occurred. If return value is non-zero, the value is
  179. an OS error code. Use the errno module or os.strerror() can be used
  180. to provide detailed error information.
  181. """
  182. return _errno(self._magic_t)
  183. def open(flags):
  184. """
  185. Returns a magic object on success and None on failure.
  186. Flags argument as for setflags.
  187. """
  188. return Magic(_open(flags))
  189. # Objects used by `detect_from_` functions
  190. mime_magic = Magic(_open(MAGIC_MIME))
  191. mime_magic.load()
  192. none_magic = Magic(_open(MAGIC_NONE))
  193. none_magic.load()
  194. def _create_filemagic(mime_detected, type_detected):
  195. splat = mime_detected.split('; ')
  196. mime_type = splat[0]
  197. if len(splat) == 2:
  198. mime_encoding = splat[1]
  199. else:
  200. mime_encoding = ''
  201. return FileMagic(name=type_detected, mime_type=mime_type,
  202. encoding=mime_encoding.replace('charset=', ''))
  203. def detect_from_filename(filename):
  204. '''Detect mime type, encoding and file type from a filename
  205. Returns a `FileMagic` namedtuple.
  206. '''
  207. return _create_filemagic(mime_magic.file(filename),
  208. none_magic.file(filename))
  209. def detect_from_fobj(fobj):
  210. '''Detect mime type, encoding and file type from file-like object
  211. Returns a `FileMagic` namedtuple.
  212. '''
  213. file_descriptor = fobj.fileno()
  214. return _create_filemagic(mime_magic.descriptor(file_descriptor),
  215. none_magic.descriptor(file_descriptor))
  216. def detect_from_content(byte_content):
  217. '''Detect mime type, encoding and file type from bytes
  218. Returns a `FileMagic` namedtuple.
  219. '''
  220. return _create_filemagic(mime_magic.buffer(byte_content),
  221. none_magic.buffer(byte_content))