1 # Access to shared MIME database
4 """This module provides access to the shared MIME database.
6 types is a dictionary of all known MIME types, indexed by the type name, e.g.
7 types['application/x-python']
9 Applications can install information about MIME types by storing an
10 XML file as <MIME>/packages/<application>.xml and running the
11 update-mime-database command, which is provided by the freedesktop.org
12 shared mime database package.
14 See http://www.freedesktop.org/standards/shared-mime-info.html for
15 information about the format of these files."""
22 from rox
.i18n
import _expand_lang
24 from xml
.dom
import Node
, minidom
32 _home
=os
.environ
['HOME']
36 _xdg_data_home
=os
.environ
['XDG_DATA_HOME']
39 _xdg_data_home
=os
.path
.join(_home
, '.local', 'share')
43 _xdg_data_dirs
=os
.environ
['XDG_DATA_DIRS']
45 _xdg_data_dirs
='/usr/local/share:/usr/share'
49 _user_install
=os
.path
.join(_xdg_data_home
, 'mime')
50 if os
.access(_user_install
, os
.R_OK
):
51 mimedirs
.append(_user_install
)
53 # See if we have the old directory
54 _old_user_install
=os
.path
.join(_home
, '.mime')
55 if os
.access(_old_user_install
, os
.R_OK
):
56 mimedirs
.append(_old_user_install
)
57 rox
.info(_("WARNING: %s not found for shared MIME database version %s, using %s for version %s") % (_user_install
, '0.11',
58 _old_user_install
, '0.10'))
60 # Neither old nor new. Assume new for installing files
61 mimedirs
.append(_user_install
)
64 for _dir
in _xdg_data_dirs
.split(':'):
65 mimedirs
.append(os
.path
.join(_dir
, 'mime'))
67 def _get_node_data(node
):
68 """Get text of XML node"""
69 return ''.join([n
.nodeValue
for n
in node
.childNodes
]).strip()
72 """Type holding data about a MIME type"""
73 def __init__(self
, media
, subtype
=None):
74 """Create the object. Call as either MIMEtype('media', 'subtype')
75 or MIMEtype('media/subtype')"""
76 if subtype
is None and media
.find('/')>0:
77 media
, subtype
=media
.split('/', 1)
82 self
.lang
=_expand_lang(os
.environ
['LANG'])
88 """Loads comment for current language. Use get_comment() instead."""
90 path
=os
.path
.join(dir, self
.media
, self
.subtype
+'.xml')
92 doc
=minidom
.parse(path
)
95 for section
in doc
.documentElement
.childNodes
:
96 if section
.nodeType
!= Node
.ELEMENT_NODE
:
98 if section
.localName
=='comment':
99 nlang
=section
.getAttribute('xml:lang')
100 if type(self
.lang
)== type(str) and nlang
!=self
.lang
:
102 if type(self
.lang
)== type(list) and nlang
not in self
.lang
:
104 self
.comment
=_get_node_data(section
)
105 self
.saved_lang
=self
.lang
110 def get_comment(self
):
111 """Returns comment for current language, loading it if needed."""
112 if self
.comment
is None or self
.lang
!=self
.saved_lang
:
120 """Return name of type, as media/subtype"""
121 return self
.media
+'/'+self
.subtype
123 """Convert to string"""
124 return self
.media
+'/'+self
.subtype
127 return '['+self
.media
+'/'+self
.subtype
+': '+self
.comment
+']'
128 return '['+self
.media
+'/'+self
.subtype
+']'
130 # Some well-known types
131 types
['text/plain']=text
=MIMEtype('text', 'plain')
132 types
['inode/blockdevice']=inode_block
=MIMEtype('inode', 'blockdevice')
133 types
['inode/chardevice']=inode_char
=MIMEtype('inode', 'chardevice')
134 types
['inode/directory']=inode_dir
=MIMEtype('inode', 'directory')
135 types
['inode/fifo']=inode_fifo
=MIMEtype('inode', 'fifo')
136 types
['inode/socket']=inode_socket
=MIMEtype('inode', 'socket')
137 types
['inode/symlink']=inode_symlink
=MIMEtype('inode', 'symlink')
138 types
['inode/door']=inode_door
=MIMEtype('inode', 'door')
139 types
['application/executable']=app_exe
=MIMEtype('application', 'executable')
141 def import_glob_file(dir):
142 """Loads name matching information from a MIME directory."""
143 path
=os
.path
.join(dir, 'globs')
145 lines
=file(path
, 'r').readlines()
153 type, pattern
=line
.split(':', 1)
162 if pattern
[:2]=='*.':
163 if pattern
[2:].find('*')<0 and pattern
[2:].find('[')<0 and pattern
[2:].find('?')<0:
164 exts
[pattern
[2:]]=mtype
165 if pattern
.find('*')<0 and pattern
.find('[')<0 and pattern
.find('?')<0:
166 literals
[pattern
]=mtype
169 import_glob_file(dir)
171 def get_type_by_name(path
):
172 """Returns type of file by its name, or None if not known"""
174 leaf
=os
.path
.basename(path
)
176 if literals
.has_key(leaf
):
177 return literals
[leaf
]
178 if literals
.has_key(lleaf
):
179 return literals
[lleaf
]
181 while ext
.find('.')>=0:
184 if exts
.has_key(ext
):
187 while ext
.find('.')>=0:
190 if exts
.has_key(ext
):
193 if fnmatch
.fnmatch(leaf
, glob
):
195 if fnmatch
.fnmatch(lleaf
, glob
):
202 def get_type(path
, follow
=1, name_pri
=100):
203 """Returns type of file indicated by path.
204 path - pathname to check (need not exist)
205 follow - when reading file, follow symbolic links
206 name_pri - Priority to do name matches. 100=override magic"""
207 # name_pri is not implemented
214 t
=get_type_by_name(path
)
218 if stat
.S_ISREG(st
.st_mode
):
219 t
=get_type_by_name(path
)
221 if stat
.S_IMODE(st
.st_mode
) & 0111:
226 elif stat
.S_ISDIR(st
.st_mode
):
228 elif stat
.S_ISCHR(st
.st_mode
):
230 elif stat
.S_ISBLK(st
.st_mode
):
232 elif stat
.S_ISFIFO(st
.st_mode
):
234 elif stat
.S_ISLNK(st
.st_mode
):
236 elif stat
.S_ISSOCK(st
.st_mode
):
240 def install_mime_info(application
, package_file
= None):
241 """Copy 'package_file' as ~/.local/share/mime/packages/<application>.xml.
242 If package_file is None, install <app_dir>/<application>.xml.
243 If already installed, does nothing. May overwrite an existing
244 file with the same name (if the contents are different)"""
245 application
+= '.xml'
247 package_file
= os
.path
.join(rox
.app_dir
, application
)
249 new_data
= file(package_file
).read()
251 # See if the file is already installed
254 test
= os
.path
.join(x
, 'packages', application
)
256 old_data
= file(test
).read()
259 if old_data
== new_data
:
260 return # Already installed
262 # Not already installed; add a new copy
264 # Create the directory structure...
266 packages
= os
.path
.join(mimedirs
[0], 'packages')
268 if not os
.path
.exists(mimedirs
[0]): os
.mkdir(mimedirs
[0])
269 if not os
.path
.exists(packages
): os
.mkdir(packages
)
272 new_file
= os
.path
.join(packages
, application
)
273 file(new_file
, 'w').write(new_data
)
275 # Update the database...
276 if os
.spawnlp(os
.P_WAIT
, 'update-mime-database', 'update-mime-database', mimedirs
[0]):
278 raise Exception(_("The 'update-mime-database' command returned an error code!\n" \
279 "Make sure you have the freedesktop.org shared MIME package:\n" \
280 "http://www.freedesktop.org/standards/shared-mime-info.html"))
282 rox
.report_exception()
284 def lookup_type(media
, subtype
=None, allow_new
=1):
285 """Return MIMEtype for given type, or None if not defined. Call as
286 either lookup_type('media', 'subtype') or lookup_type('media/subtype')"""
290 type=media
+'/'+subtype
292 if types
.has_key(type):
295 return MIMEtype(media
, subtype
)
299 """Print results for name. Test routine"""
301 print name
, t
, t
.get_comment()
303 if __name__
=='__main__':
308 for f
in sys
.argv
[1:]: