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