]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/file/python/magic.py
MFC r308420: MFV r308392: file 5.29.
[FreeBSD/stable/9.git] / contrib / file / python / magic.py
1 # coding: utf-8
2
3 '''
4 Python bindings for libmagic
5 '''
6
7 import ctypes
8
9 from collections import namedtuple
10
11 from ctypes import *
12 from ctypes.util import find_library
13
14
15 def _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
26 MAGIC_NONE = NONE = 0
27 MAGIC_DEBUG = DEBUG = 1
28 MAGIC_SYMLINK = SYMLINK = 2
29 MAGIC_COMPRESS = COMPRESS = 4
30 MAGIC_DEVICES = DEVICES = 8
31 MAGIC_MIME_TYPE = MIME_TYPE = 16
32 MAGIC_CONTINUE = CONTINUE = 32
33 MAGIC_CHECK = CHECK = 64
34 MAGIC_PRESERVE_ATIME = PRESERVE_ATIME = 128
35 MAGIC_RAW = RAW = 256
36 MAGIC_ERROR = ERROR = 512
37 MAGIC_MIME_ENCODING = MIME_ENCODING = 1024
38 MAGIC_MIME = MIME = 1040  # MIME_TYPE + MIME_ENCODING
39 MAGIC_APPLE = APPLE = 2048
40
41 MAGIC_NO_CHECK_COMPRESS = NO_CHECK_COMPRESS = 4096
42 MAGIC_NO_CHECK_TAR = NO_CHECK_TAR = 8192
43 MAGIC_NO_CHECK_SOFT = NO_CHECK_SOFT = 16384
44 MAGIC_NO_CHECK_APPTYPE = NO_CHECK_APPTYPE = 32768
45 MAGIC_NO_CHECK_ELF = NO_CHECK_ELF = 65536
46 MAGIC_NO_CHECK_TEXT = NO_CHECK_TEXT = 131072
47 MAGIC_NO_CHECK_CDF = NO_CHECK_CDF = 262144
48 MAGIC_NO_CHECK_TOKENS = NO_CHECK_TOKENS = 1048576
49 MAGIC_NO_CHECK_ENCODING = NO_CHECK_ENCODING = 2097152
50
51 MAGIC_NO_CHECK_BUILTIN = NO_CHECK_BUILTIN = 4173824
52
53 FileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name'))
54
55
56 class magic_set(Structure):
57     pass
58 magic_set._fields_ = []
59 magic_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
110 class 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, '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, '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, '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
227 def 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
236 mime_magic = Magic(_open(MAGIC_MIME))
237 mime_magic.load()
238 none_magic = Magic(_open(MAGIC_NONE))
239 none_magic.load()
240
241
242 def _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
249 def 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
259 def 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
270 def 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))