1300899Sdelphij# coding: utf-8
2300899Sdelphij
3267843Sdelphij'''
4267843SdelphijPython bindings for libmagic
5267843Sdelphij'''
6267843Sdelphij
7267843Sdelphijimport ctypes
8267843Sdelphij
9300899Sdelphijfrom collections import namedtuple
10300899Sdelphij
11267843Sdelphijfrom ctypes import *
12267843Sdelphijfrom ctypes.util import find_library
13267843Sdelphij
14267843Sdelphij
15267843Sdelphijdef _init():
16267843Sdelphij    """
17267843Sdelphij    Loads the shared library through ctypes and returns a library
18267843Sdelphij    L{ctypes.CDLL} instance
19267843Sdelphij    """
20267843Sdelphij    return ctypes.cdll.LoadLibrary(find_library('magic'))
21267843Sdelphij
22267843Sdelphij_libraries = {}
23267843Sdelphij_libraries['magic'] = _init()
24267843Sdelphij
25267843Sdelphij# Flag constants for open and setflags
26267843SdelphijMAGIC_NONE = NONE = 0
27267843SdelphijMAGIC_DEBUG = DEBUG = 1
28267843SdelphijMAGIC_SYMLINK = SYMLINK = 2
29267843SdelphijMAGIC_COMPRESS = COMPRESS = 4
30267843SdelphijMAGIC_DEVICES = DEVICES = 8
31267843SdelphijMAGIC_MIME_TYPE = MIME_TYPE = 16
32267843SdelphijMAGIC_CONTINUE = CONTINUE = 32
33267843SdelphijMAGIC_CHECK = CHECK = 64
34267843SdelphijMAGIC_PRESERVE_ATIME = PRESERVE_ATIME = 128
35267843SdelphijMAGIC_RAW = RAW = 256
36267843SdelphijMAGIC_ERROR = ERROR = 512
37267843SdelphijMAGIC_MIME_ENCODING = MIME_ENCODING = 1024
38300899SdelphijMAGIC_MIME = MIME = 1040  # MIME_TYPE + MIME_ENCODING
39267843SdelphijMAGIC_APPLE = APPLE = 2048
40267843Sdelphij
41267843SdelphijMAGIC_NO_CHECK_COMPRESS = NO_CHECK_COMPRESS = 4096
42267843SdelphijMAGIC_NO_CHECK_TAR = NO_CHECK_TAR = 8192
43267843SdelphijMAGIC_NO_CHECK_SOFT = NO_CHECK_SOFT = 16384
44267843SdelphijMAGIC_NO_CHECK_APPTYPE = NO_CHECK_APPTYPE = 32768
45267843SdelphijMAGIC_NO_CHECK_ELF = NO_CHECK_ELF = 65536
46267843SdelphijMAGIC_NO_CHECK_TEXT = NO_CHECK_TEXT = 131072
47267843SdelphijMAGIC_NO_CHECK_CDF = NO_CHECK_CDF = 262144
48267843SdelphijMAGIC_NO_CHECK_TOKENS = NO_CHECK_TOKENS = 1048576
49267843SdelphijMAGIC_NO_CHECK_ENCODING = NO_CHECK_ENCODING = 2097152
50267843Sdelphij
51267843SdelphijMAGIC_NO_CHECK_BUILTIN = NO_CHECK_BUILTIN = 4173824
52267843Sdelphij
53300899SdelphijFileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name'))
54267843Sdelphij
55300899Sdelphij
56267843Sdelphijclass magic_set(Structure):
57267843Sdelphij    pass
58267843Sdelphijmagic_set._fields_ = []
59267843Sdelphijmagic_t = POINTER(magic_set)
60267843Sdelphij
61267843Sdelphij_open = _libraries['magic'].magic_open
62267843Sdelphij_open.restype = magic_t
63267843Sdelphij_open.argtypes = [c_int]
64267843Sdelphij
65267843Sdelphij_close = _libraries['magic'].magic_close
66267843Sdelphij_close.restype = None
67267843Sdelphij_close.argtypes = [magic_t]
68267843Sdelphij
69267843Sdelphij_file = _libraries['magic'].magic_file
70267843Sdelphij_file.restype = c_char_p
71267843Sdelphij_file.argtypes = [magic_t, c_char_p]
72267843Sdelphij
73267843Sdelphij_descriptor = _libraries['magic'].magic_descriptor
74267843Sdelphij_descriptor.restype = c_char_p
75267843Sdelphij_descriptor.argtypes = [magic_t, c_int]
76267843Sdelphij
77267843Sdelphij_buffer = _libraries['magic'].magic_buffer
78267843Sdelphij_buffer.restype = c_char_p
79267843Sdelphij_buffer.argtypes = [magic_t, c_void_p, c_size_t]
80267843Sdelphij
81267843Sdelphij_error = _libraries['magic'].magic_error
82267843Sdelphij_error.restype = c_char_p
83267843Sdelphij_error.argtypes = [magic_t]
84267843Sdelphij
85267843Sdelphij_setflags = _libraries['magic'].magic_setflags
86267843Sdelphij_setflags.restype = c_int
87267843Sdelphij_setflags.argtypes = [magic_t, c_int]
88267843Sdelphij
89267843Sdelphij_load = _libraries['magic'].magic_load
90267843Sdelphij_load.restype = c_int
91267843Sdelphij_load.argtypes = [magic_t, c_char_p]
92267843Sdelphij
93267843Sdelphij_compile = _libraries['magic'].magic_compile
94267843Sdelphij_compile.restype = c_int
95267843Sdelphij_compile.argtypes = [magic_t, c_char_p]
96267843Sdelphij
97267843Sdelphij_check = _libraries['magic'].magic_check
98267843Sdelphij_check.restype = c_int
99267843Sdelphij_check.argtypes = [magic_t, c_char_p]
100267843Sdelphij
101267843Sdelphij_list = _libraries['magic'].magic_list
102267843Sdelphij_list.restype = c_int
103267843Sdelphij_list.argtypes = [magic_t, c_char_p]
104267843Sdelphij
105267843Sdelphij_errno = _libraries['magic'].magic_errno
106267843Sdelphij_errno.restype = c_int
107267843Sdelphij_errno.argtypes = [magic_t]
108267843Sdelphij
109267843Sdelphij
110267843Sdelphijclass Magic(object):
111267843Sdelphij    def __init__(self, ms):
112267843Sdelphij        self._magic_t = ms
113267843Sdelphij
114267843Sdelphij    def close(self):
115267843Sdelphij        """
116267843Sdelphij        Closes the magic database and deallocates any resources used.
117267843Sdelphij        """
118267843Sdelphij        _close(self._magic_t)
119267843Sdelphij
120328875Seadler    @staticmethod
121328875Seadler    def __tostr(s):
122328875Seadler        if s is None:
123328875Seadler            return None
124328875Seadler        if isinstance(s, str):
125328875Seadler            return s
126328875Seadler        try:  # keep Python 2 compatibility
127328875Seadler            return str(s, 'utf-8')
128328875Seadler        except TypeError:
129328875Seadler            return str(s)
130328875Seadler
131328875Seadler    @staticmethod
132328875Seadler    def __tobytes(b):
133328875Seadler        if b is None:
134328875Seadler            return None
135328875Seadler        if isinstance(b, bytes):
136328875Seadler            return b
137328875Seadler        try:  # keep Python 2 compatibility
138328875Seadler            return bytes(b, 'utf-8')
139328875Seadler        except TypeError:
140328875Seadler            return bytes(b)
141328875Seadler
142267843Sdelphij    def file(self, filename):
143267843Sdelphij        """
144267843Sdelphij        Returns a textual description of the contents of the argument passed
145267843Sdelphij        as a filename or None if an error occurred and the MAGIC_ERROR flag
146328875Seadler        is set. A call to errno() will return the numeric error code.
147267843Sdelphij        """
148328875Seadler        return Magic.__tostr(_file(self._magic_t, Magic.__tobytes(filename)))
149267843Sdelphij
150267843Sdelphij    def descriptor(self, fd):
151267843Sdelphij        """
152328875Seadler        Returns a textual description of the contents of the argument passed
153328875Seadler        as a file descriptor or None if an error occurred and the MAGIC_ERROR
154328875Seadler        flag is set. A call to errno() will return the numeric error code.
155267843Sdelphij        """
156328875Seadler        return Magic.__tostr(_descriptor(self._magic_t, fd))
157267843Sdelphij
158267843Sdelphij    def buffer(self, buf):
159267843Sdelphij        """
160267843Sdelphij        Returns a textual description of the contents of the argument passed
161267843Sdelphij        as a buffer or None if an error occurred and the MAGIC_ERROR flag
162267843Sdelphij        is set. A call to errno() will return the numeric error code.
163267843Sdelphij        """
164328875Seadler        return Magic.__tostr(_buffer(self._magic_t, buf, len(buf)))
165267843Sdelphij
166267843Sdelphij    def error(self):
167267843Sdelphij        """
168267843Sdelphij        Returns a textual explanation of the last error or None
169267843Sdelphij        if there was no error.
170267843Sdelphij        """
171328875Seadler        return Magic.__tostr(_error(self._magic_t))
172267843Sdelphij
173267843Sdelphij    def setflags(self, flags):
174267843Sdelphij        """
175267843Sdelphij        Set flags on the magic object which determine how magic checking
176267843Sdelphij        behaves; a bitwise OR of the flags described in libmagic(3), but
177267843Sdelphij        without the MAGIC_ prefix.
178267843Sdelphij
179267843Sdelphij        Returns -1 on systems that don't support utime(2) or utimes(2)
180267843Sdelphij        when PRESERVE_ATIME is set.
181267843Sdelphij        """
182267843Sdelphij        return _setflags(self._magic_t, flags)
183267843Sdelphij
184267843Sdelphij    def load(self, filename=None):
185267843Sdelphij        """
186267843Sdelphij        Must be called to load entries in the colon separated list of database
187267843Sdelphij        files passed as argument or the default database file if no argument
188267843Sdelphij        before any magic queries can be performed.
189267843Sdelphij
190267843Sdelphij        Returns 0 on success and -1 on failure.
191267843Sdelphij        """
192328875Seadler        return _load(self._magic_t, Magic.__tobytes(filename))
193267843Sdelphij
194267843Sdelphij    def compile(self, dbs):
195267843Sdelphij        """
196267843Sdelphij        Compile entries in the colon separated list of database files
197267843Sdelphij        passed as argument or the default database file if no argument.
198267843Sdelphij        The compiled files created are named from the basename(1) of each file
199267843Sdelphij        argument with ".mgc" appended to it.
200328875Seadler
201328875Seadler        Returns 0 on success and -1 on failure.
202267843Sdelphij        """
203328875Seadler        return _compile(self._magic_t, Magic.__tobytes(dbs))
204267843Sdelphij
205267843Sdelphij    def check(self, dbs):
206267843Sdelphij        """
207267843Sdelphij        Check the validity of entries in the colon separated list of
208267843Sdelphij        database files passed as argument or the default database file
209267843Sdelphij        if no argument.
210328875Seadler
211267843Sdelphij        Returns 0 on success and -1 on failure.
212267843Sdelphij        """
213328875Seadler        return _check(self._magic_t, Magic.__tobytes(dbs))
214267843Sdelphij
215267843Sdelphij    def list(self, dbs):
216267843Sdelphij        """
217267843Sdelphij        Check the validity of entries in the colon separated list of
218267843Sdelphij        database files passed as argument or the default database file
219267843Sdelphij        if no argument.
220328875Seadler
221267843Sdelphij        Returns 0 on success and -1 on failure.
222267843Sdelphij        """
223328875Seadler        return _list(self._magic_t, Magic.__tobytes(dbs))
224267843Sdelphij
225267843Sdelphij    def errno(self):
226267843Sdelphij        """
227267843Sdelphij        Returns a numeric error code. If return value is 0, an internal
228267843Sdelphij        magic error occurred. If return value is non-zero, the value is
229267843Sdelphij        an OS error code. Use the errno module or os.strerror() can be used
230267843Sdelphij        to provide detailed error information.
231267843Sdelphij        """
232267843Sdelphij        return _errno(self._magic_t)
233267843Sdelphij
234267843Sdelphij
235267843Sdelphijdef open(flags):
236267843Sdelphij    """
237267843Sdelphij    Returns a magic object on success and None on failure.
238267843Sdelphij    Flags argument as for setflags.
239267843Sdelphij    """
240267843Sdelphij    return Magic(_open(flags))
241300899Sdelphij
242300899Sdelphij
243300899Sdelphij# Objects used by `detect_from_` functions
244300899Sdelphijmime_magic = Magic(_open(MAGIC_MIME))
245300899Sdelphijmime_magic.load()
246300899Sdelphijnone_magic = Magic(_open(MAGIC_NONE))
247300899Sdelphijnone_magic.load()
248300899Sdelphij
249300899Sdelphij
250300899Sdelphijdef _create_filemagic(mime_detected, type_detected):
251300899Sdelphij    mime_type, mime_encoding = mime_detected.split('; ')
252300899Sdelphij
253300899Sdelphij    return FileMagic(name=type_detected, mime_type=mime_type,
254300899Sdelphij                     encoding=mime_encoding.replace('charset=', ''))
255300899Sdelphij
256300899Sdelphij
257300899Sdelphijdef detect_from_filename(filename):
258300899Sdelphij    '''Detect mime type, encoding and file type from a filename
259300899Sdelphij
260300899Sdelphij    Returns a `FileMagic` namedtuple.
261300899Sdelphij    '''
262300899Sdelphij
263300899Sdelphij    return _create_filemagic(mime_magic.file(filename),
264300899Sdelphij                             none_magic.file(filename))
265300899Sdelphij
266300899Sdelphij
267300899Sdelphijdef detect_from_fobj(fobj):
268300899Sdelphij    '''Detect mime type, encoding and file type from file-like object
269300899Sdelphij
270300899Sdelphij    Returns a `FileMagic` namedtuple.
271300899Sdelphij    '''
272300899Sdelphij
273300899Sdelphij    file_descriptor = fobj.fileno()
274300899Sdelphij    return _create_filemagic(mime_magic.descriptor(file_descriptor),
275300899Sdelphij                             none_magic.descriptor(file_descriptor))
276300899Sdelphij
277300899Sdelphij
278300899Sdelphijdef detect_from_content(byte_content):
279300899Sdelphij    '''Detect mime type, encoding and file type from bytes
280300899Sdelphij
281300899Sdelphij    Returns a `FileMagic` namedtuple.
282300899Sdelphij    '''
283300899Sdelphij
284300899Sdelphij    return _create_filemagic(mime_magic.buffer(byte_content),
285300899Sdelphij                             none_magic.buffer(byte_content))
286