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