1
2
3 """This module encapsulates mime operations.
4 """
5
6 __version__ = "$Revision: 1.27 $"
7 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
8 __license__ = "GPL"
9
10
11 import sys
12 import os
13 import mailcap
14 import mimetypes
15 import subprocess
16 import shutil
17 import logging
18
19
20
21 if __name__ == '__main__':
22 sys.path.insert(0, '../../')
23 import gmShellAPI, gmTools, gmCfg2
24
25
26 _log = logging.getLogger('gm.docs')
27 _log.info(__version__)
28
30 """Guess mime type of arbitrary file.
31
32 filenames are supposed to be in Unicode
33 """
34 worst_case = "application/octet-stream"
35
36
37 try:
38 import extractor
39 xtract = extractor.Extractor()
40 props = xtract.extract(filename = aFileName)
41 for prop, val in props:
42 if (prop == 'mimetype') and (val != worst_case):
43 return val
44 except ImportError:
45 _log.exception('Python wrapper for libextractor not installed.')
46
47 ret_code = -1
48
49
50
51
52 mime_guesser_cmd = u'file -i -b "%s"' % aFileName
53
54
55 aPipe = os.popen(mime_guesser_cmd.encode(sys.getfilesystemencoding()), 'r')
56 if aPipe is None:
57 _log.debug("cannot open pipe to [%s]" % mime_guesser_cmd)
58 else:
59 pipe_output = aPipe.readline().replace('\n', '').strip()
60 ret_code = aPipe.close()
61 if ret_code is None:
62 _log.debug('[%s]: <%s>' % (mime_guesser_cmd, pipe_output))
63 if pipe_output not in [u'', worst_case]:
64 return pipe_output
65 else:
66 _log.error('[%s] on %s (%s): failed with exit(%s)' % (mime_guesser_cmd, os.name, sys.platform, ret_code))
67
68
69 mime_guesser_cmd = 'extract -p mimetype "%s"' % aFileName
70 aPipe = os.popen(mime_guesser_cmd.encode(sys.getfilesystemencoding()), 'r')
71 if aPipe is None:
72 _log.debug("cannot open pipe to [%s]" % mime_guesser_cmd)
73 else:
74 pipe_output = aPipe.readline()[11:].replace('\n', '').strip()
75 ret_code = aPipe.close()
76 if ret_code is None:
77 _log.debug('[%s]: <%s>' % (mime_guesser_cmd, pipe_output))
78 if pipe_output not in [u'', worst_case]:
79 return pipe_output
80 else:
81 _log.error('[%s] on %s (%s): failed with exit(%s)' % (mime_guesser_cmd, os.name, sys.platform, ret_code))
82
83
84
85
86
87 _log.info("OS level mime detection failed, falling back to built-in magic")
88
89 import gmMimeMagic
90 mime_type = gmTools.coalesce(gmMimeMagic.file(aFileName), worst_case)
91 del gmMimeMagic
92
93 _log.debug('"%s" -> <%s>' % (aFileName, mime_type))
94 return mime_type
95
97 """Return command for viewer for this mime type complete with this file"""
98
99 if aFileName is None:
100 _log.error("You should specify a file name for the replacement of %s.")
101
102
103 aFileName = """%s"""
104
105 mailcaps = mailcap.getcaps()
106 (viewer, junk) = mailcap.findmatch(mailcaps, aMimeType, key = 'view', filename = '%s' % aFileName)
107
108
109 _log.debug("<%s> viewer: [%s]" % (aMimeType, viewer))
110
111 return viewer
112
114
115 if filename is None:
116 _log.error("You should specify a file name for the replacement of %s.")
117
118
119 filename = """%s"""
120
121 mailcaps = mailcap.getcaps()
122 (editor, junk) = mailcap.findmatch(mailcaps, mimetype, key = 'edit', filename = '%s' % filename)
123
124
125
126 _log.debug("<%s> editor: [%s]" % (mimetype, editor))
127
128 return editor
129
131 """Return file extension based on what the OS thinks a file of this mimetype should end in."""
132
133
134 ext = mimetypes.guess_extension(mimetype)
135 if ext is not None:
136 _log.debug('<%s>: *.%s' % (mimetype, ext))
137 return ext
138
139 _log.error("<%s>: no suitable file extension known to the OS" % mimetype)
140
141
142 cfg = gmCfg2.gmCfgData()
143 ext = cfg.get (
144 group = u'extensions',
145 option = mimetype,
146 source_order = [('user-mime', 'return'), ('system-mime', 'return')]
147 )
148
149 if ext is not None:
150 _log.debug('<%s>: *.%s (%s)' % (mimetype, ext, candidate))
151 return ext
152
153 _log.error("<%s>: no suitable file extension found in config files" % mimetype)
154
155 return ext
156
158 if aFile is None:
159 return None
160
161 (path_name, f_ext) = os.path.splitext(aFile)
162 if f_ext != '':
163 return f_ext
164
165
166 mime_type = guess_mimetype(aFile)
167 f_ext = guess_ext_by_mimetype(mime_type)
168 if f_ext is None:
169 _log.error('unable to guess file extension for mime type [%s]' % mime_type)
170 return None
171
172 return f_ext
173
174 _system_startfile_cmd = None
175
176 open_cmds = {
177 'xdg-open': 'xdg-open "%s"',
178 'kfmclient': 'kfmclient exec "%s"',
179 'gnome-open': 'gnome-open "%s"',
180 'exo-open': 'exo-open "%s"',
181 'op': 'op "%s"',
182 'open': 'open "%s"'
183
184
185 }
186
188
189 global _system_startfile_cmd
190
191 if _system_startfile_cmd == u'':
192 return False, None
193
194 if _system_startfile_cmd is not None:
195 return True, _system_startfile_cmd % filename
196
197 open_cmd_candidates = ['xdg-open', 'kfmclient', 'gnome-open', 'exo-open', 'op', 'open']
198
199 for candidate in open_cmd_candidates:
200 found, binary = gmShellAPI.detect_external_binary(binary = candidate)
201 if not found:
202 continue
203 _system_startfile_cmd = open_cmds[candidate]
204 _log.info('detected local startfile cmd: [%s]', _system_startfile_cmd)
205 return True, _system_startfile_cmd % filename
206
207 _system_startfile_cmd = u''
208 return False, None
209
210 -def convert_file(filename=None, target_mime=None, target_filename=None, target_extension=None):
211 """Convert file from one format into another.
212
213 target_mime: a mime type
214 """
215 if target_extension is None:
216 tmp, target_extension = os.path.splitext(target_filename)
217
218 base_name = u'gm-convert_file'
219
220 paths = gmTools.gmPaths()
221 local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', base_name)
222
223 candidates = [ base_name, local_script ]
224 found, binary = gmShellAPI.find_first_binary(binaries = candidates)
225 if not found:
226 binary = base_name
227
228 cmd_line = [
229 binary,
230 filename,
231 target_mime,
232 target_extension.strip('.'),
233 target_filename
234 ]
235 _log.debug('converting: %s', cmd_line)
236 try:
237 gm_convert = subprocess.Popen(cmd_line)
238 except OSError:
239 _log.debug('cannot run <%s(.bat)>', base_name)
240 return False
241 gm_convert.communicate()
242 if gm_convert.returncode != 0:
243 _log.error('<%s(.bat)> returned [%s], failed to convert', base_name, gm_convert.returncode)
244 return False
245
246 return True
247
249 """Try to find an appropriate viewer with all tricks and call it.
250
251 block: try to detach from viewer or not, None means to use mailcap default
252 """
253
254 try:
255 open(aFile).close()
256 except:
257 _log.exception('cannot read [%s]', aFile)
258 msg = _('[%s] is not a readable file') % aFile
259 return False, msg
260
261
262 found, startfile_cmd = _get_system_startfile_cmd(aFile)
263 if found:
264 if gmShellAPI.run_command_in_shell(command = startfile_cmd, blocking = block):
265 return True, ''
266
267 mime_type = guess_mimetype(aFile)
268 viewer_cmd = get_viewer_cmd(mime_type, aFile)
269
270 if viewer_cmd is not None:
271 if gmShellAPI.run_command_in_shell(command=viewer_cmd, blocking=block):
272 return True, ''
273
274 _log.warning("no viewer found via standard mailcap system")
275 if os.name == "posix":
276 _log.warning("you should add a viewer for this mime type to your mailcap file")
277 _log.info("let's see what the OS can do about that")
278
279
280 (path_name, f_ext) = os.path.splitext(aFile)
281
282 if f_ext in ['', '.tmp']:
283
284 f_ext = guess_ext_by_mimetype(mime_type)
285 if f_ext is None:
286 _log.warning("no suitable file extension found, trying anyway")
287 file_to_display = aFile
288 f_ext = '?unknown?'
289 else:
290 file_to_display = aFile + f_ext
291 shutil.copyfile(aFile, file_to_display)
292
293 else:
294 file_to_display = aFile
295
296 file_to_display = os.path.normpath(file_to_display)
297 _log.debug("file %s <type %s> (ext %s) -> file %s" % (aFile, mime_type, f_ext, file_to_display))
298
299 try:
300 os.startfile(file_to_display)
301 except:
302 _log.exception('os.startfile(%s) failed', file_to_display)
303 msg = _("Unable to display the file:\n\n"
304 " [%s]\n\n"
305 "Your system does not seem to have a (working)\n"
306 "viewer registered for the file type\n"
307 " [%s]"
308 ) % (file_to_display, mime_type)
309 return False, msg
310
311
312
313
314
315 return True, ''
316
317 if __name__ == "__main__":
318
319 if len(sys.argv) > 1 and sys.argv[1] == u'test':
320
321 filename = sys.argv[2]
322
323 _get_system_startfile_cmd(filename)
324 print _system_startfile_cmd
325
326
327
328