1# coding: utf-8 2 3''' 4Python bindings for libmagic 5''' 6 7import ctypes 8import threading 9 10from collections import namedtuple 11 12from ctypes import * 13from ctypes.util import find_library 14 15 16def _init(): 17 """ 18 Loads the shared library through ctypes and returns a library 19 L{ctypes.CDLL} instance 20 """ 21 return ctypes.cdll.LoadLibrary(find_library('magic')) 22 23_libraries = {} 24_libraries['magic'] = _init() 25 26# Flag constants for open and setflags 27MAGIC_NONE = NONE = 0 28MAGIC_DEBUG = DEBUG = 1 29MAGIC_SYMLINK = SYMLINK = 2 30MAGIC_COMPRESS = COMPRESS = 4 31MAGIC_DEVICES = DEVICES = 8 32MAGIC_MIME_TYPE = MIME_TYPE = 16 33MAGIC_CONTINUE = CONTINUE = 32 34MAGIC_CHECK = CHECK = 64 35MAGIC_PRESERVE_ATIME = PRESERVE_ATIME = 128 36MAGIC_RAW = RAW = 256 37MAGIC_ERROR = ERROR = 512 38MAGIC_MIME_ENCODING = MIME_ENCODING = 1024 39MAGIC_MIME = MIME = 1040 # MIME_TYPE + MIME_ENCODING 40MAGIC_APPLE = APPLE = 2048 41 42MAGIC_NO_CHECK_COMPRESS = NO_CHECK_COMPRESS = 4096 43MAGIC_NO_CHECK_TAR = NO_CHECK_TAR = 8192 44MAGIC_NO_CHECK_SOFT = NO_CHECK_SOFT = 16384 45MAGIC_NO_CHECK_APPTYPE = NO_CHECK_APPTYPE = 32768 46MAGIC_NO_CHECK_ELF = NO_CHECK_ELF = 65536 47MAGIC_NO_CHECK_TEXT = NO_CHECK_TEXT = 131072 48MAGIC_NO_CHECK_CDF = NO_CHECK_CDF = 262144 49MAGIC_NO_CHECK_TOKENS = NO_CHECK_TOKENS = 1048576 50MAGIC_NO_CHECK_ENCODING = NO_CHECK_ENCODING = 2097152 51 52MAGIC_NO_CHECK_BUILTIN = NO_CHECK_BUILTIN = 4173824 53 54MAGIC_PARAM_INDIR_MAX = PARAM_INDIR_MAX = 0 55MAGIC_PARAM_NAME_MAX = PARAM_NAME_MAX = 1 56MAGIC_PARAM_ELF_PHNUM_MAX = PARAM_ELF_PHNUM_MAX = 2 57MAGIC_PARAM_ELF_SHNUM_MAX = PARAM_ELF_SHNUM_MAX = 3 58MAGIC_PARAM_ELF_NOTES_MAX = PARAM_ELF_NOTES_MAX = 4 59MAGIC_PARAM_REGEX_MAX = PARAM_REGEX_MAX = 5 60MAGIC_PARAM_BYTES_MAX = PARAM_BYTES_MAX = 6 61 62FileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name')) 63 64 65class magic_set(Structure): 66 pass 67magic_set._fields_ = [] 68magic_t = POINTER(magic_set) 69 70_open = _libraries['magic'].magic_open 71_open.restype = magic_t 72_open.argtypes = [c_int] 73 74_close = _libraries['magic'].magic_close 75_close.restype = None 76_close.argtypes = [magic_t] 77 78_file = _libraries['magic'].magic_file 79_file.restype = c_char_p 80_file.argtypes = [magic_t, c_char_p] 81 82_descriptor = _libraries['magic'].magic_descriptor 83_descriptor.restype = c_char_p 84_descriptor.argtypes = [magic_t, c_int] 85 86_buffer = _libraries['magic'].magic_buffer 87_buffer.restype = c_char_p 88_buffer.argtypes = [magic_t, c_void_p, c_size_t] 89 90_error = _libraries['magic'].magic_error 91_error.restype = c_char_p 92_error.argtypes = [magic_t] 93 94_setflags = _libraries['magic'].magic_setflags 95_setflags.restype = c_int 96_setflags.argtypes = [magic_t, c_int] 97 98_load = _libraries['magic'].magic_load 99_load.restype = c_int 100_load.argtypes = [magic_t, c_char_p] 101 102_compile = _libraries['magic'].magic_compile 103_compile.restype = c_int 104_compile.argtypes = [magic_t, c_char_p] 105 106_check = _libraries['magic'].magic_check 107_check.restype = c_int 108_check.argtypes = [magic_t, c_char_p] 109 110_list = _libraries['magic'].magic_list 111_list.restype = c_int 112_list.argtypes = [magic_t, c_char_p] 113 114_errno = _libraries['magic'].magic_errno 115_errno.restype = c_int 116_errno.argtypes = [magic_t] 117 118_getparam = _libraries['magic'].magic_getparam 119_getparam.restype = c_int 120_getparam.argtypes = [magic_t, c_int, c_void_p] 121 122_setparam = _libraries['magic'].magic_setparam 123_setparam.restype = c_int 124_setparam.argtypes = [magic_t, c_int, c_void_p] 125 126 127class Magic(object): 128 def __init__(self, ms): 129 self._magic_t = ms 130 131 def close(self): 132 """ 133 Closes the magic database and deallocates any resources used. 134 """ 135 _close(self._magic_t) 136 137 @staticmethod 138 def __tostr(s): 139 if s is None: 140 return None 141 if isinstance(s, str): 142 return s 143 try: # keep Python 2 compatibility 144 return str(s, 'utf-8') 145 except TypeError: 146 return str(s) 147 148 @staticmethod 149 def __tobytes(b): 150 if b is None: 151 return None 152 if isinstance(b, bytes): 153 return b 154 try: # keep Python 2 compatibility 155 return bytes(b, 'utf-8') 156 except TypeError: 157 return bytes(b) 158 159 def file(self, filename): 160 """ 161 Returns a textual description of the contents of the argument passed 162 as a filename or None if an error occurred and the MAGIC_ERROR flag 163 is set. A call to errno() will return the numeric error code. 164 """ 165 return Magic.__tostr(_file(self._magic_t, Magic.__tobytes(filename))) 166 167 def descriptor(self, fd): 168 """ 169 Returns a textual description of the contents of the argument passed 170 as a file descriptor or None if an error occurred and the MAGIC_ERROR 171 flag is set. A call to errno() will return the numeric error code. 172 """ 173 return Magic.__tostr(_descriptor(self._magic_t, fd)) 174 175 def buffer(self, buf): 176 """ 177 Returns a textual description of the contents of the argument passed 178 as a buffer or None if an error occurred and the MAGIC_ERROR flag 179 is set. A call to errno() will return the numeric error code. 180 """ 181 return Magic.__tostr(_buffer(self._magic_t, buf, len(buf))) 182 183 def error(self): 184 """ 185 Returns a textual explanation of the last error or None 186 if there was no error. 187 """ 188 return Magic.__tostr(_error(self._magic_t)) 189 190 def setflags(self, flags): 191 """ 192 Set flags on the magic object which determine how magic checking 193 behaves; a bitwise OR of the flags described in libmagic(3), but 194 without the MAGIC_ prefix. 195 196 Returns -1 on systems that don't support utime(2) or utimes(2) 197 when PRESERVE_ATIME is set. 198 """ 199 return _setflags(self._magic_t, flags) 200 201 def load(self, filename=None): 202 """ 203 Must be called to load entries in the colon separated list of database 204 files passed as argument or the default database file if no argument 205 before any magic queries can be performed. 206 207 Returns 0 on success and -1 on failure. 208 """ 209 return _load(self._magic_t, Magic.__tobytes(filename)) 210 211 def compile(self, dbs): 212 """ 213 Compile entries in the colon separated list of database files 214 passed as argument or the default database file if no argument. 215 The compiled files created are named from the basename(1) of each file 216 argument with ".mgc" appended to it. 217 218 Returns 0 on success and -1 on failure. 219 """ 220 return _compile(self._magic_t, Magic.__tobytes(dbs)) 221 222 def check(self, dbs): 223 """ 224 Check the validity of entries in the colon separated list of 225 database files passed as argument or the default database file 226 if no argument. 227 228 Returns 0 on success and -1 on failure. 229 """ 230 return _check(self._magic_t, Magic.__tobytes(dbs)) 231 232 def list(self, dbs): 233 """ 234 Check the validity of entries in the colon separated list of 235 database files passed as argument or the default database file 236 if no argument. 237 238 Returns 0 on success and -1 on failure. 239 """ 240 return _list(self._magic_t, Magic.__tobytes(dbs)) 241 242 def errno(self): 243 """ 244 Returns a numeric error code. If return value is 0, an internal 245 magic error occurred. If return value is non-zero, the value is 246 an OS error code. Use the errno module or os.strerror() can be used 247 to provide detailed error information. 248 """ 249 return _errno(self._magic_t) 250 251 def getparam(self, param): 252 """ 253 Returns the param value if successful and -1 if the parameter 254 was unknown. 255 """ 256 v = c_int() 257 i = _getparam(self._magic_t, param, byref(v)) 258 if i == -1: 259 return -1 260 return v.value 261 262 def setparam(self, param, value): 263 """ 264 Returns 0 if successful and -1 if the parameter was unknown. 265 """ 266 v = c_int(value) 267 return _setparam(self._magic_t, param, byref(v)) 268 269 270def open(flags): 271 """ 272 Returns a magic object on success and None on failure. 273 Flags argument as for setflags. 274 """ 275 return Magic(_open(flags)) 276 277 278# Objects used by `detect_from_` functions 279class MagicDetect(object): 280 def __init__(self): 281 self.mime_magic = Magic(_open(MAGIC_MIME)) 282 self.mime_magic.load() 283 self.none_magic = Magic(_open(MAGIC_NONE)) 284 self.none_magic.load() 285 286 def __del__(self): 287 self.mime_magic.close() 288 self.none_magic.close() 289 290threadlocal = threading.local() 291 292def _detect_make(): 293 v = getattr(threadlocal, "magic_instance", None) 294 if v is None: 295 v = MagicDetect() 296 setattr(threadlocal, "magic_instance", v) 297 return v 298 299def _create_filemagic(mime_detected, type_detected): 300 try: 301 mime_type, mime_encoding = mime_detected.split('; ') 302 except ValueError: 303 raise ValueError(mime_detected) 304 305 return FileMagic(name=type_detected, mime_type=mime_type, 306 encoding=mime_encoding.replace('charset=', '')) 307 308 309def detect_from_filename(filename): 310 '''Detect mime type, encoding and file type from a filename 311 312 Returns a `FileMagic` namedtuple. 313 ''' 314 x = _detect_make() 315 return _create_filemagic(x.mime_magic.file(filename), 316 x.none_magic.file(filename)) 317 318 319def detect_from_fobj(fobj): 320 '''Detect mime type, encoding and file type from file-like object 321 322 Returns a `FileMagic` namedtuple. 323 ''' 324 325 file_descriptor = fobj.fileno() 326 x = _detect_make() 327 return _create_filemagic(x.mime_magic.descriptor(file_descriptor), 328 x.none_magic.descriptor(file_descriptor)) 329 330 331def detect_from_content(byte_content): 332 '''Detect mime type, encoding and file type from bytes 333 334 Returns a `FileMagic` namedtuple. 335 ''' 336 337 x = _detect_make() 338 return _create_filemagic(x.mime_magic.buffer(byte_content), 339 x.none_magic.buffer(byte_content)) 340