magic.py revision 298920
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
53FileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name'))
54
55
56class magic_set(Structure):
57    pass
58magic_set._fields_ = []
59magic_t = POINTER(magic_set)
60
61_open = _libraries['magic'].magic_open
62_open.restype = magic_t
63_open.argtypes = [c_int]
64
65_close = _libraries['magic'].magic_close
66_close.restype = None
67_close.argtypes = [magic_t]
68
69_file = _libraries['magic'].magic_file
70_file.restype = c_char_p
71_file.argtypes = [magic_t, c_char_p]
72
73_descriptor = _libraries['magic'].magic_descriptor
74_descriptor.restype = c_char_p
75_descriptor.argtypes = [magic_t, c_int]
76
77_buffer = _libraries['magic'].magic_buffer
78_buffer.restype = c_char_p
79_buffer.argtypes = [magic_t, c_void_p, c_size_t]
80
81_error = _libraries['magic'].magic_error
82_error.restype = c_char_p
83_error.argtypes = [magic_t]
84
85_setflags = _libraries['magic'].magic_setflags
86_setflags.restype = c_int
87_setflags.argtypes = [magic_t, c_int]
88
89_load = _libraries['magic'].magic_load
90_load.restype = c_int
91_load.argtypes = [magic_t, c_char_p]
92
93_compile = _libraries['magic'].magic_compile
94_compile.restype = c_int
95_compile.argtypes = [magic_t, c_char_p]
96
97_check = _libraries['magic'].magic_check
98_check.restype = c_int
99_check.argtypes = [magic_t, c_char_p]
100
101_list = _libraries['magic'].magic_list
102_list.restype = c_int
103_list.argtypes = [magic_t, c_char_p]
104
105_errno = _libraries['magic'].magic_errno
106_errno.restype = c_int
107_errno.argtypes = [magic_t]
108
109
110class Magic(object):
111    def __init__(self, ms):
112        self._magic_t = ms
113
114    def close(self):
115        """
116        Closes the magic database and deallocates any resources used.
117        """
118        _close(self._magic_t)
119
120    def file(self, filename):
121        """
122        Returns a textual description of the contents of the argument passed
123        as a filename 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        if isinstance(filename, bytes):
127            bi = filename
128        else:
129            try:  # keep Python 2 compatibility
130                bi = bytes(filename, 'utf-8')
131            except TypeError:
132                bi = bytes(filename)
133        r = _file(self._magic_t, bi)
134        if isinstance(r, str):
135            return r
136        else:
137            return str(r).encode('utf-8')
138
139    def descriptor(self, fd):
140        """
141        Like the file method, but the argument is a file descriptor.
142        """
143        return _descriptor(self._magic_t, fd)
144
145    def buffer(self, buf):
146        """
147        Returns a textual description of the contents of the argument passed
148        as a buffer or None if an error occurred and the MAGIC_ERROR flag
149        is set. A call to errno() will return the numeric error code.
150        """
151        r = _buffer(self._magic_t, buf, len(buf))
152        if isinstance(r, str):
153            return r
154        else:
155            return str(r).encode('utf-8')
156
157    def error(self):
158        """
159        Returns a textual explanation of the last error or None
160        if there was no error.
161        """
162        e = _error(self._magic_t)
163        if isinstance(e, str):
164            return e
165        else:
166            return str(e).encode('utf-8')
167
168    def setflags(self, flags):
169        """
170        Set flags on the magic object which determine how magic checking
171        behaves; a bitwise OR of the flags described in libmagic(3), but
172        without the MAGIC_ prefix.
173
174        Returns -1 on systems that don't support utime(2) or utimes(2)
175        when PRESERVE_ATIME is set.
176        """
177        return _setflags(self._magic_t, flags)
178
179    def load(self, filename=None):
180        """
181        Must be called to load entries in the colon separated list of database
182        files passed as argument or the default database file if no argument
183        before any magic queries can be performed.
184
185        Returns 0 on success and -1 on failure.
186        """
187        return _load(self._magic_t, filename)
188
189    def compile(self, dbs):
190        """
191        Compile entries in the colon separated list of database files
192        passed as argument or the default database file if no argument.
193        Returns 0 on success and -1 on failure.
194        The compiled files created are named from the basename(1) of each file
195        argument with ".mgc" appended to it.
196        """
197        return _compile(self._magic_t, dbs)
198
199    def check(self, dbs):
200        """
201        Check the validity of entries in the colon separated list of
202        database files passed as argument or the default database file
203        if no argument.
204        Returns 0 on success and -1 on failure.
205        """
206        return _check(self._magic_t, dbs)
207
208    def list(self, dbs):
209        """
210        Check the validity of entries in the colon separated list of
211        database files passed as argument or the default database file
212        if no argument.
213        Returns 0 on success and -1 on failure.
214        """
215        return _list(self._magic_t, dbs)
216
217    def errno(self):
218        """
219        Returns a numeric error code. If return value is 0, an internal
220        magic error occurred. If return value is non-zero, the value is
221        an OS error code. Use the errno module or os.strerror() can be used
222        to provide detailed error information.
223        """
224        return _errno(self._magic_t)
225
226
227def open(flags):
228    """
229    Returns a magic object on success and None on failure.
230    Flags argument as for setflags.
231    """
232    return Magic(_open(flags))
233
234
235# Objects used by `detect_from_` functions
236mime_magic = Magic(_open(MAGIC_MIME))
237mime_magic.load()
238none_magic = Magic(_open(MAGIC_NONE))
239none_magic.load()
240
241
242def _create_filemagic(mime_detected, type_detected):
243    mime_type, mime_encoding = mime_detected.split('; ')
244
245    return FileMagic(name=type_detected, mime_type=mime_type,
246                     encoding=mime_encoding.replace('charset=', ''))
247
248
249def detect_from_filename(filename):
250    '''Detect mime type, encoding and file type from a filename
251
252    Returns a `FileMagic` namedtuple.
253    '''
254
255    return _create_filemagic(mime_magic.file(filename),
256                             none_magic.file(filename))
257
258
259def detect_from_fobj(fobj):
260    '''Detect mime type, encoding and file type from file-like object
261
262    Returns a `FileMagic` namedtuple.
263    '''
264
265    file_descriptor = fobj.fileno()
266    return _create_filemagic(mime_magic.descriptor(file_descriptor),
267                             none_magic.descriptor(file_descriptor))
268
269
270def detect_from_content(byte_content):
271    '''Detect mime type, encoding and file type from bytes
272
273    Returns a `FileMagic` namedtuple.
274    '''
275
276    return _create_filemagic(mime_magic.buffer(byte_content),
277                             none_magic.buffer(byte_content))
278