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 self._close = _close 131 132 def close(self): 133 """ 134 Closes the magic database and deallocates any resources used. 135 """ 136 if _close: 137 _close(self._magic_t) 138 139 @staticmethod 140 def __tostr(s): 141 if s is None: 142 return None 143 if isinstance(s, str): 144 return s 145 try: # keep Python 2 compatibility 146 return str(s, 'utf-8') 147 except TypeError: 148 return str(s) 149 150 @staticmethod 151 def __tobytes(b): 152 if b is None: 153 return None 154 if isinstance(b, bytes): 155 return b 156 try: # keep Python 2 compatibility 157 return bytes(b, 'utf-8') 158 except TypeError: 159 return bytes(b) 160 161 def file(self, filename): 162 """ 163 Returns a textual description of the contents of the argument passed 164 as a filename or None if an error occurred and the MAGIC_ERROR flag 165 is set. A call to errno() will return the numeric error code. 166 """ 167 return Magic.__tostr(_file(self._magic_t, Magic.__tobytes(filename))) 168 169 def descriptor(self, fd): 170 """ 171 Returns a textual description of the contents of the argument passed 172 as a file descriptor or None if an error occurred and the MAGIC_ERROR 173 flag is set. A call to errno() will return the numeric error code. 174 """ 175 return Magic.__tostr(_descriptor(self._magic_t, fd)) 176 177 def buffer(self, buf): 178 """ 179 Returns a textual description of the contents of the argument passed 180 as a buffer or None if an error occurred and the MAGIC_ERROR flag 181 is set. A call to errno() will return the numeric error code. 182 """ 183 return Magic.__tostr(_buffer(self._magic_t, buf, len(buf))) 184 185 def error(self): 186 """ 187 Returns a textual explanation of the last error or None 188 if there was no error. 189 """ 190 return Magic.__tostr(_error(self._magic_t)) 191 192 def setflags(self, flags): 193 """ 194 Set flags on the magic object which determine how magic checking 195 behaves; a bitwise OR of the flags described in libmagic(3), but 196 without the MAGIC_ prefix. 197 198 Returns -1 on systems that don't support utime(2) or utimes(2) 199 when PRESERVE_ATIME is set. 200 """ 201 return _setflags(self._magic_t, flags) 202 203 def load(self, filename=None): 204 """ 205 Must be called to load entries in the colon separated list of database 206 files passed as argument or the default database file if no argument 207 before any magic queries can be performed. 208 209 Returns 0 on success and -1 on failure. 210 """ 211 return _load(self._magic_t, Magic.__tobytes(filename)) 212 213 def compile(self, dbs): 214 """ 215 Compile entries in the colon separated list of database files 216 passed as argument or the default database file if no argument. 217 The compiled files created are named from the basename(1) of each file 218 argument with ".mgc" appended to it. 219 220 Returns 0 on success and -1 on failure. 221 """ 222 return _compile(self._magic_t, Magic.__tobytes(dbs)) 223 224 def check(self, dbs): 225 """ 226 Check the validity of entries in the colon separated list of 227 database files passed as argument or the default database file 228 if no argument. 229 230 Returns 0 on success and -1 on failure. 231 """ 232 return _check(self._magic_t, Magic.__tobytes(dbs)) 233 234 def list(self, dbs): 235 """ 236 Check the validity of entries in the colon separated list of 237 database files passed as argument or the default database file 238 if no argument. 239 240 Returns 0 on success and -1 on failure. 241 """ 242 return _list(self._magic_t, Magic.__tobytes(dbs)) 243 244 def errno(self): 245 """ 246 Returns a numeric error code. If return value is 0, an internal 247 magic error occurred. If return value is non-zero, the value is 248 an OS error code. Use the errno module or os.strerror() can be used 249 to provide detailed error information. 250 """ 251 return _errno(self._magic_t) 252 253 def getparam(self, param): 254 """ 255 Returns the param value if successful and -1 if the parameter 256 was unknown. 257 """ 258 v = c_int() 259 i = _getparam(self._magic_t, param, byref(v)) 260 if i == -1: 261 return -1 262 return v.value 263 264 def setparam(self, param, value): 265 """ 266 Returns 0 if successful and -1 if the parameter was unknown. 267 """ 268 v = c_int(value) 269 return _setparam(self._magic_t, param, byref(v)) 270 271 272def open(flags): 273 """ 274 Returns a magic object on success and None on failure. 275 Flags argument as for setflags. 276 """ 277 magic_t = _open(flags) 278 if magic_t is None: 279 return None 280 return Magic(magic_t) 281 282 283# Objects used by `detect_from_` functions 284class error(Exception): 285 pass 286 287class MagicDetect(object): 288 def __init__(self): 289 self.mime_magic = open(MAGIC_MIME) 290 if self.mime_magic is None: 291 raise error 292 if self.mime_magic.load() == -1: 293 self.mime_magic.close() 294 self.mime_magic = None 295 raise error 296 self.none_magic = open(MAGIC_NONE) 297 if self.none_magic is None: 298 self.mime_magic.close() 299 self.mime_magic = None 300 raise error 301 if self.none_magic.load() == -1: 302 self.none_magic.close() 303 self.none_magic = None 304 self.mime_magic.close() 305 self.mime_magic = None 306 raise error 307 308 def __del__(self): 309 if self.mime_magic is not None: 310 self.mime_magic.close() 311 if self.none_magic is not None: 312 self.none_magic.close() 313 314threadlocal = threading.local() 315 316def _detect_make(): 317 v = getattr(threadlocal, "magic_instance", None) 318 if v is None: 319 v = MagicDetect() 320 setattr(threadlocal, "magic_instance", v) 321 return v 322 323def _create_filemagic(mime_detected, type_detected): 324 try: 325 mime_type, mime_encoding = mime_detected.split('; ') 326 except ValueError: 327 raise ValueError(mime_detected) 328 329 return FileMagic(name=type_detected, mime_type=mime_type, 330 encoding=mime_encoding.replace('charset=', '')) 331 332 333def detect_from_filename(filename): 334 '''Detect mime type, encoding and file type from a filename 335 336 Returns a `FileMagic` namedtuple. 337 ''' 338 x = _detect_make() 339 return _create_filemagic(x.mime_magic.file(filename), 340 x.none_magic.file(filename)) 341 342 343def detect_from_fobj(fobj): 344 '''Detect mime type, encoding and file type from file-like object 345 346 Returns a `FileMagic` namedtuple. 347 ''' 348 349 file_descriptor = fobj.fileno() 350 x = _detect_make() 351 return _create_filemagic(x.mime_magic.descriptor(file_descriptor), 352 x.none_magic.descriptor(file_descriptor)) 353 354 355def detect_from_content(byte_content): 356 '''Detect mime type, encoding and file type from bytes 357 358 Returns a `FileMagic` namedtuple. 359 ''' 360 361 x = _detect_make() 362 return _create_filemagic(x.mime_magic.buffer(byte_content), 363 x.none_magic.buffer(byte_content)) 364