xref: /netbsd-src/external/bsd/file/dist/python/magic.py (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
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