Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/file/python/magic.py
39478 views
1
# coding: utf-8
2
3
'''
4
Python bindings for libmagic
5
'''
6
7
import ctypes
8
import threading
9
10
from collections import namedtuple
11
12
from ctypes import *
13
from ctypes.util import find_library
14
15
16
def _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
27
MAGIC_NONE = NONE = 0
28
MAGIC_DEBUG = DEBUG = 1
29
MAGIC_SYMLINK = SYMLINK = 2
30
MAGIC_COMPRESS = COMPRESS = 4
31
MAGIC_DEVICES = DEVICES = 8
32
MAGIC_MIME_TYPE = MIME_TYPE = 16
33
MAGIC_CONTINUE = CONTINUE = 32
34
MAGIC_CHECK = CHECK = 64
35
MAGIC_PRESERVE_ATIME = PRESERVE_ATIME = 128
36
MAGIC_RAW = RAW = 256
37
MAGIC_ERROR = ERROR = 512
38
MAGIC_MIME_ENCODING = MIME_ENCODING = 1024
39
MAGIC_MIME = MIME = 1040 # MIME_TYPE + MIME_ENCODING
40
MAGIC_APPLE = APPLE = 2048
41
42
MAGIC_NO_CHECK_COMPRESS = NO_CHECK_COMPRESS = 4096
43
MAGIC_NO_CHECK_TAR = NO_CHECK_TAR = 8192
44
MAGIC_NO_CHECK_SOFT = NO_CHECK_SOFT = 16384
45
MAGIC_NO_CHECK_APPTYPE = NO_CHECK_APPTYPE = 32768
46
MAGIC_NO_CHECK_ELF = NO_CHECK_ELF = 65536
47
MAGIC_NO_CHECK_TEXT = NO_CHECK_TEXT = 131072
48
MAGIC_NO_CHECK_CDF = NO_CHECK_CDF = 262144
49
MAGIC_NO_CHECK_TOKENS = NO_CHECK_TOKENS = 1048576
50
MAGIC_NO_CHECK_ENCODING = NO_CHECK_ENCODING = 2097152
51
52
MAGIC_NO_CHECK_BUILTIN = NO_CHECK_BUILTIN = 4173824
53
54
MAGIC_PARAM_INDIR_MAX = PARAM_INDIR_MAX = 0
55
MAGIC_PARAM_NAME_MAX = PARAM_NAME_MAX = 1
56
MAGIC_PARAM_ELF_PHNUM_MAX = PARAM_ELF_PHNUM_MAX = 2
57
MAGIC_PARAM_ELF_SHNUM_MAX = PARAM_ELF_SHNUM_MAX = 3
58
MAGIC_PARAM_ELF_NOTES_MAX = PARAM_ELF_NOTES_MAX = 4
59
MAGIC_PARAM_REGEX_MAX = PARAM_REGEX_MAX = 5
60
MAGIC_PARAM_BYTES_MAX = PARAM_BYTES_MAX = 6
61
62
FileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name'))
63
64
65
class magic_set(Structure):
66
pass
67
magic_set._fields_ = []
68
magic_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
127
class Magic(object):
128
def __init__(self, ms):
129
self._magic_t = ms
130
131
def close(self):
132
"""
133
Closes the magic database and deallocates any resources used.
134
"""
135
_close(self._magic_t)
136
137
@staticmethod
138
def __tostr(s):
139
if s is None:
140
return None
141
if isinstance(s, str):
142
return s
143
try: # keep Python 2 compatibility
144
return str(s, 'utf-8')
145
except TypeError:
146
return str(s)
147
148
@staticmethod
149
def __tobytes(b):
150
if b is None:
151
return None
152
if isinstance(b, bytes):
153
return b
154
try: # keep Python 2 compatibility
155
return bytes(b, 'utf-8')
156
except TypeError:
157
return bytes(b)
158
159
def file(self, filename):
160
"""
161
Returns a textual description of the contents of the argument passed
162
as a filename or None if an error occurred and the MAGIC_ERROR flag
163
is set. A call to errno() will return the numeric error code.
164
"""
165
return Magic.__tostr(_file(self._magic_t, Magic.__tobytes(filename)))
166
167
def descriptor(self, fd):
168
"""
169
Returns a textual description of the contents of the argument passed
170
as a file descriptor or None if an error occurred and the MAGIC_ERROR
171
flag is set. A call to errno() will return the numeric error code.
172
"""
173
return Magic.__tostr(_descriptor(self._magic_t, fd))
174
175
def buffer(self, buf):
176
"""
177
Returns a textual description of the contents of the argument passed
178
as a buffer or None if an error occurred and the MAGIC_ERROR flag
179
is set. A call to errno() will return the numeric error code.
180
"""
181
return Magic.__tostr(_buffer(self._magic_t, buf, len(buf)))
182
183
def error(self):
184
"""
185
Returns a textual explanation of the last error or None
186
if there was no error.
187
"""
188
return Magic.__tostr(_error(self._magic_t))
189
190
def setflags(self, flags):
191
"""
192
Set flags on the magic object which determine how magic checking
193
behaves; a bitwise OR of the flags described in libmagic(3), but
194
without the MAGIC_ prefix.
195
196
Returns -1 on systems that don't support utime(2) or utimes(2)
197
when PRESERVE_ATIME is set.
198
"""
199
return _setflags(self._magic_t, flags)
200
201
def load(self, filename=None):
202
"""
203
Must be called to load entries in the colon separated list of database
204
files passed as argument or the default database file if no argument
205
before any magic queries can be performed.
206
207
Returns 0 on success and -1 on failure.
208
"""
209
return _load(self._magic_t, Magic.__tobytes(filename))
210
211
def compile(self, dbs):
212
"""
213
Compile entries in the colon separated list of database files
214
passed as argument or the default database file if no argument.
215
The compiled files created are named from the basename(1) of each file
216
argument with ".mgc" appended to it.
217
218
Returns 0 on success and -1 on failure.
219
"""
220
return _compile(self._magic_t, Magic.__tobytes(dbs))
221
222
def check(self, dbs):
223
"""
224
Check the validity of entries in the colon separated list of
225
database files passed as argument or the default database file
226
if no argument.
227
228
Returns 0 on success and -1 on failure.
229
"""
230
return _check(self._magic_t, Magic.__tobytes(dbs))
231
232
def list(self, dbs):
233
"""
234
Check the validity of entries in the colon separated list of
235
database files passed as argument or the default database file
236
if no argument.
237
238
Returns 0 on success and -1 on failure.
239
"""
240
return _list(self._magic_t, Magic.__tobytes(dbs))
241
242
def errno(self):
243
"""
244
Returns a numeric error code. If return value is 0, an internal
245
magic error occurred. If return value is non-zero, the value is
246
an OS error code. Use the errno module or os.strerror() can be used
247
to provide detailed error information.
248
"""
249
return _errno(self._magic_t)
250
251
def getparam(self, param):
252
"""
253
Returns the param value if successful and -1 if the parameter
254
was unknown.
255
"""
256
v = c_int()
257
i = _getparam(self._magic_t, param, byref(v))
258
if i == -1:
259
return -1
260
return v.value
261
262
def setparam(self, param, value):
263
"""
264
Returns 0 if successful and -1 if the parameter was unknown.
265
"""
266
v = c_int(value)
267
return _setparam(self._magic_t, param, byref(v))
268
269
270
def open(flags):
271
"""
272
Returns a magic object on success and None on failure.
273
Flags argument as for setflags.
274
"""
275
magic_t = _open(flags)
276
if magic_t is None:
277
return None
278
return Magic(magic_t)
279
280
281
# Objects used by `detect_from_` functions
282
class error(Exception):
283
pass
284
285
class MagicDetect(object):
286
def __init__(self):
287
self.mime_magic = open(MAGIC_MIME)
288
if self.mime_magic is None:
289
raise error
290
if self.mime_magic.load() == -1:
291
self.mime_magic.close()
292
self.mime_magic = None
293
raise error
294
self.none_magic = open(MAGIC_NONE)
295
if self.none_magic is None:
296
self.mime_magic.close()
297
self.mime_magic = None
298
raise error
299
if self.none_magic.load() == -1:
300
self.none_magic.close()
301
self.none_magic = None
302
self.mime_magic.close()
303
self.mime_magic = None
304
raise error
305
306
def __del__(self):
307
if self.mime_magic is not None:
308
self.mime_magic.close()
309
if self.none_magic is not None:
310
self.none_magic.close()
311
312
threadlocal = threading.local()
313
314
def _detect_make():
315
v = getattr(threadlocal, "magic_instance", None)
316
if v is None:
317
v = MagicDetect()
318
setattr(threadlocal, "magic_instance", v)
319
return v
320
321
def _create_filemagic(mime_detected, type_detected):
322
try:
323
mime_type, mime_encoding = mime_detected.split('; ')
324
except ValueError:
325
raise ValueError(mime_detected)
326
327
return FileMagic(name=type_detected, mime_type=mime_type,
328
encoding=mime_encoding.replace('charset=', ''))
329
330
331
def detect_from_filename(filename):
332
'''Detect mime type, encoding and file type from a filename
333
334
Returns a `FileMagic` namedtuple.
335
'''
336
x = _detect_make()
337
return _create_filemagic(x.mime_magic.file(filename),
338
x.none_magic.file(filename))
339
340
341
def detect_from_fobj(fobj):
342
'''Detect mime type, encoding and file type from file-like object
343
344
Returns a `FileMagic` namedtuple.
345
'''
346
347
file_descriptor = fobj.fileno()
348
x = _detect_make()
349
return _create_filemagic(x.mime_magic.descriptor(file_descriptor),
350
x.none_magic.descriptor(file_descriptor))
351
352
353
def detect_from_content(byte_content):
354
'''Detect mime type, encoding and file type from bytes
355
356
Returns a `FileMagic` namedtuple.
357
'''
358
359
x = _detect_make()
360
return _create_filemagic(x.mime_magic.buffer(byte_content),
361
x.none_magic.buffer(byte_content))
362
363