From 0c3f127c86af818d260966d2292b199757087157 Mon Sep 17 00:00:00 2001 From: Simon Poirier Date: Sat, 11 Jul 2009 21:39:46 +0000 Subject: repackage --- (limited to 'src/sugar/bundle') diff --git a/src/sugar/bundle/Makefile.am b/src/sugar/bundle/Makefile.am deleted file mode 100644 index f1af791..0000000 --- a/src/sugar/bundle/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -sugardir = $(pythondir)/sugar/bundle -sugar_PYTHON = \ - __init__.py \ - bundle.py \ - activitybundle.py \ - contentbundle.py diff --git a/src/sugar/bundle/__init__.py b/src/sugar/bundle/__init__.py deleted file mode 100644 index 85ebced..0000000 --- a/src/sugar/bundle/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (C) 2006-2007, Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. diff --git a/src/sugar/bundle/activitybundle.py b/src/sugar/bundle/activitybundle.py deleted file mode 100644 index eb35307..0000000 --- a/src/sugar/bundle/activitybundle.py +++ /dev/null @@ -1,375 +0,0 @@ -# Copyright (C) 2007, Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -"""Sugar activity bundles - -UNSTABLE. -""" - -from ConfigParser import ConfigParser -import locale -import os -import tempfile -import logging - -from sugar import env -from sugar import util -from sugar.bundle.bundle import Bundle, \ - MalformedBundleException, NotInstalledException - -class ActivityBundle(Bundle): - """A Sugar activity bundle - - See http://wiki.laptop.org/go/Activity_bundles for details - """ - - MIME_TYPE = 'application/vnd.olpc-sugar' - DEPRECATED_MIME_TYPE = 'application/vnd.olpc-x-sugar' - - _zipped_extension = '.xo' - _unzipped_extension = '.activity' - _infodir = 'activity' - - def __init__(self, path): - Bundle.__init__(self, path) - self.activity_class = None - self.bundle_exec = None - - self._name = None - self._icon = None - self._bundle_id = None - self._mime_types = None - self._show_launcher = True - self._activity_version = 0 - self._installation_time = os.stat(path).st_mtime - self._manifest = None - - info_file = self.get_file('activity/activity.info') - if info_file is None: - raise MalformedBundleException('No activity.info file') - self._parse_info(info_file) - - linfo_file = self._get_linfo_file() - if linfo_file: - self._parse_linfo(linfo_file) - - def _get_manifest(self): - if self._manifest is None: - self._manifest = self._read_manifest() - return self._manifest - - manifest = property(_get_manifest, None, None, - "NOTICE: this property is potentially quite slow, so better make sure " - "that it's not called at performance-critical points like shell or " - "activity startup.") - - def _raw_manifest(self): - f = self.get_file("MANIFEST") - if not f: - logging.warning("Activity directory lacks a MANIFEST file.") - return [] - - ret = [line.strip() for line in f.readlines()] - f.close() - return ret - - def _read_manifest(self): - """return a list with the lines in MANIFEST, with invalid lines replaced - by empty lines. - - Since absolute order carries information on file history, it should - be preserved. For instance, when renaming a file, you should leave - the new name on the same line as the old one. - """ - logging.debug('STARTUP: Reading manifest') - lines = self._raw_manifest() - - # Remove trailing newlines, they do not help keep absolute position. - while lines and lines[-1] == "": - lines = lines[:-1] - - for num, line in enumerate(lines): - if not line: - continue - - # Remove duplicates - if line in lines[0:num]: - lines[num] = "" - logging.warning("Bundle %s: duplicate entry in MANIFEST: %s" - % (self._name,line)) - continue - - # Remove MANIFEST - if line == "MANIFEST": - lines[num] = "" - logging.warning("Bundle %s: MANIFEST includes itself: %s" - % (self._name,line)) - - # Remove invalid files - if not self.is_file(line): - lines[num] = "" - logging.warning("Bundle %s: invalid entry in MANIFEST: %s" - % (self._name,line)) - - return lines - - def get_files(self, manifest = None): - files = [line for line in (manifest or self.manifest) if line] - - if self.is_file('MANIFEST'): - files.append('MANIFEST') - - return files - - def _parse_info(self, info_file): - cp = ConfigParser() - cp.readfp(info_file) - - section = 'Activity' - - if cp.has_option(section, 'bundle_id'): - self._bundle_id = cp.get(section, 'bundle_id') - # FIXME deprecated - elif cp.has_option(section, 'service_name'): - self._bundle_id = cp.get(section, 'service_name') - else: - raise MalformedBundleException( - 'Activity bundle %s does not specify a bundle id' % - self._path) - - if cp.has_option(section, 'name'): - self._name = cp.get(section, 'name') - else: - raise MalformedBundleException( - 'Activity bundle %s does not specify a name' % self._path) - - # FIXME class is deprecated - if cp.has_option(section, 'class'): - self.activity_class = cp.get(section, 'class') - elif cp.has_option(section, 'exec'): - self.bundle_exec = cp.get(section, 'exec') - else: - raise MalformedBundleException( - 'Activity bundle %s must specify either class or exec' % - self._path) - - if cp.has_option(section, 'mime_types'): - mime_list = cp.get(section, 'mime_types').strip(';') - self._mime_types = [ mime.strip() for mime in mime_list.split(';') ] - - if cp.has_option(section, 'show_launcher'): - if cp.get(section, 'show_launcher') == 'no': - self._show_launcher = False - - if cp.has_option(section, 'icon'): - self._icon = cp.get(section, 'icon') - - if cp.has_option(section, 'activity_version'): - version = cp.get(section, 'activity_version') - try: - self._activity_version = int(version) - except ValueError: - raise MalformedBundleException( - 'Activity bundle %s has invalid version number %s' % - (self._path, version)) - - def _get_linfo_file(self): - lang = locale.getdefaultlocale()[0] - if not lang: - return None - - linfo_path = os.path.join('locale', lang, 'activity.linfo') - linfo_file = self.get_file(linfo_path) - if linfo_file is not None: - return linfo_file - - linfo_path = os.path.join('locale', lang[:2], 'activity.linfo') - linfo_file = self.get_file(linfo_path) - if linfo_file is not None: - return linfo_file - - return None - - def _parse_linfo(self, linfo_file): - cp = ConfigParser() - cp.readfp(linfo_file) - - section = 'Activity' - - if cp.has_option(section, 'name'): - self._name = cp.get(section, 'name') - - def get_locale_path(self): - """Get the locale path inside the (installed) activity bundle.""" - if self._zip_file is not None: - raise NotInstalledException - return os.path.join(self._path, 'locale') - - def get_icons_path(self): - """Get the icons path inside the (installed) activity bundle.""" - if self._zip_file is not None: - raise NotInstalledException - return os.path.join(self._path, 'icons') - - def get_path(self): - """Get the activity bundle path.""" - return self._path - - def get_name(self): - """Get the activity user visible name.""" - return self._name - - def get_installation_time(self): - """Get a timestamp representing the time at which this activity was - installed.""" - return self._installation_time - - def get_bundle_id(self): - """Get the activity bundle id""" - return self._bundle_id - - # FIXME: this should return the icon data, not a filename, so that - # we don't need to create a temp file in the zip case - def get_icon(self): - """Get the activity icon name""" - icon_path = os.path.join('activity', self._icon + '.svg') - if self._zip_file is None: - return os.path.join(self._path, icon_path) - else: - icon_data = self.get_file(icon_path).read() - temp_file, temp_file_path = tempfile.mkstemp(self._icon) - os.write(temp_file, icon_data) - os.close(temp_file) - return util.TempFilePath(temp_file_path) - - def get_activity_version(self): - """Get the activity version""" - return self._activity_version - - def get_command(self): - """Get the command to execute to launch the activity factory""" - if self.bundle_exec: - command = os.path.expandvars(self.bundle_exec) - else: - command = 'sugar-activity ' + self.activity_class - - return command - - def get_mime_types(self): - """Get the MIME types supported by the activity""" - return self._mime_types - - def get_show_launcher(self): - """Get whether there should be a visible launcher for the activity""" - return self._show_launcher - - def install(self, install_dir=None, strict_manifest=False): - if install_dir is None: - install_dir = env.get_user_activities_path() - - self._unzip(install_dir) - - install_path = os.path.join(install_dir, self._zip_root_dir) - - # List installed files - manifestfiles = self.get_files(self._raw_manifest()) - paths = [] - for root, dirs_, files in os.walk(install_path): - rel_path = root[len(install_path) + 1:] - for f in files: - paths.append(os.path.join(rel_path, f)) - - # Check the list against the MANIFEST - for path in paths: - if path in manifestfiles: - manifestfiles.remove(path) - elif path != "MANIFEST": - logging.warning("Bundle %s: %s not in MANIFEST"% - (self._name,path)) - if strict_manifest: - os.remove(os.path.join(install_path, path)) - - # Is anything in MANIFEST left over after accounting for all files? - if manifestfiles: - err = ("Bundle %s: files in MANIFEST not included: %s"% - (self._name,str(manifestfiles))) - if strict_manifest: - raise MalformedBundleException(err) - else: - logging.warning(err) - - xdg_data_home = os.getenv('XDG_DATA_HOME', - os.path.expanduser('~/.local/share')) - - mime_path = os.path.join(install_path, 'activity', 'mimetypes.xml') - if os.path.isfile(mime_path): - mime_dir = os.path.join(xdg_data_home, 'mime') - mime_pkg_dir = os.path.join(mime_dir, 'packages') - if not os.path.isdir(mime_pkg_dir): - os.makedirs(mime_pkg_dir) - installed_mime_path = os.path.join(mime_pkg_dir, - '%s.xml' % self._bundle_id) - os.symlink(mime_path, installed_mime_path) - os.spawnlp(os.P_WAIT, 'update-mime-database', - 'update-mime-database', mime_dir) - - mime_types = self.get_mime_types() - if mime_types is not None: - installed_icons_dir = os.path.join(xdg_data_home, - 'icons/sugar/scalable/mimetypes') - if not os.path.isdir(installed_icons_dir): - os.makedirs(installed_icons_dir) - - for mime_type in mime_types: - mime_icon_base = os.path.join(install_path, 'activity', - mime_type.replace('/', '-')) - svg_file = mime_icon_base + '.svg' - info_file = mime_icon_base + '.icon' - if os.path.isfile(svg_file): - os.symlink(svg_file, - os.path.join(installed_icons_dir, - os.path.basename(svg_file))) - if os.path.isfile(info_file): - os.symlink(info_file, - os.path.join(installed_icons_dir, - os.path.basename(info_file))) - return install_path - - def uninstall(self, install_path, force=False): - xdg_data_home = os.getenv('XDG_DATA_HOME', - os.path.expanduser('~/.local/share')) - - mime_dir = os.path.join(xdg_data_home, 'mime') - installed_mime_path = os.path.join(mime_dir, 'packages', - '%s.xml' % self._bundle_id) - if os.path.exists(installed_mime_path): - os.remove(installed_mime_path) - os.spawnlp(os.P_WAIT, 'update-mime-database', - 'update-mime-database', mime_dir) - - mime_types = self.get_mime_types() - if mime_types is not None: - installed_icons_dir = os.path.join(xdg_data_home, - 'icons/sugar/scalable/mimetypes') - if os.path.isdir(installed_icons_dir): - for f in os.listdir(installed_icons_dir): - path = os.path.join(installed_icons_dir, f) - if os.path.islink(path) and \ - os.readlink(path).startswith(install_path): - os.remove(path) - - self._uninstall(install_path) diff --git a/src/sugar/bundle/bundle.py b/src/sugar/bundle/bundle.py deleted file mode 100644 index a1b2686..0000000 --- a/src/sugar/bundle/bundle.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright (C) 2007, Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -"""Sugar bundle file handler - -UNSTABLE. -""" - -import os -import logging -import shutil -import StringIO -import zipfile - -class AlreadyInstalledException(Exception): - pass - -class NotInstalledException(Exception): - pass - -class InvalidPathException(Exception): - pass - -class ZipExtractException(Exception): - pass - -class RegistrationException(Exception): - pass - -class MalformedBundleException(Exception): - pass - -class Bundle(object): - """A Sugar activity, content module, etc. - - The bundle itself may be either a zip file or a directory - hierarchy, with metadata about the bundle stored various files - inside it. - - This is an abstract base class. See ActivityBundle and - ContentBundle for more details on those bundle types. - """ - - _zipped_extension = None - _unzipped_extension = None - - def __init__(self, path): - self._path = path - self._zip_root_dir = None - - if os.path.isdir(self._path): - self._zip_file = None - else: - self._zip_file = zipfile.ZipFile(self._path) - self._check_zip_bundle() - - # manifest = self._get_file(self._infodir + '/contents') - # if manifest is None: - # raise MalformedBundleException('No manifest file') - # - # signature = self._get_file(self._infodir + '/contents.sig') - # if signature is None: - # raise MalformedBundleException('No signature file') - - def __del__(self): - if self._zip_file is not None: - self._zip_file.close() - - def _check_zip_bundle(self): - file_names = self._zip_file.namelist() - if len(file_names) == 0: - raise MalformedBundleException('Empty zip file') - - if file_names[0] == 'mimetype': - del file_names[0] - - self._zip_root_dir = file_names[0].split('/')[0] - if self._zip_root_dir.startswith('.'): - raise MalformedBundleException( - 'root directory starts with .') - if self._unzipped_extension is not None: - (name_, ext) = os.path.splitext(self._zip_root_dir) - if ext != self._unzipped_extension: - raise MalformedBundleException( - 'All files in the bundle must be inside a single ' + - 'directory whose name ends with "%s"' % - self._unzipped_extension) - - for file_name in file_names: - if not file_name.startswith(self._zip_root_dir): - raise MalformedBundleException( - 'All files in the bundle must be inside a single ' + - 'top-level directory') - - def get_file(self, filename): - f = None - - if self._zip_file is None: - path = os.path.join(self._path, filename) - try: - f = open(path,"rb") - except IOError: - return None - else: - path = os.path.join(self._zip_root_dir, filename) - try: - data = self._zip_file.read(path) - f = StringIO.StringIO(data) - except KeyError: - logging.debug('%s not found.' % filename) - - return f - - def is_file(self, filename): - if self._zip_file is None: - path = os.path.join(self._path, filename) - return os.path.isfile(path) - else: - path = os.path.join(self._zip_root_dir, filename) - try: - self._zip_file.getinfo(path) - except KeyError: - return False - - return True - - def is_dir(self, filename): - if self._zip_file is None: - path = os.path.join(self._path, filename) - return os.path.isdir(path) - else: - path = os.path.join(self._zip_root_dir, filename, "") - for f in self._zip_file.namelist(): - if f.startswith(path): - return True - return False - - def get_path(self): - """Get the bundle path.""" - return self._path - - def _unzip(self, install_dir): - if self._zip_file is None: - raise AlreadyInstalledException - - if not os.path.isdir(install_dir): - os.mkdir(install_dir, 0775) - - # zipfile provides API that in theory would let us do this - # correctly by hand, but handling all the oddities of - # Windows/UNIX mappings, extension attributes, deprecated - # features, etc makes it impractical. - # FIXME: use manifest - if os.spawnlp(os.P_WAIT, 'unzip', 'unzip', '-o', self._path, - '-x', 'mimetype', '-d', install_dir): - # clean up install dir after failure - shutil.rmtree(os.path.join(install_dir, self._zip_root_dir), - ignore_errors=True) - # indicate failure. - raise ZipExtractException - - def _zip(self, bundle_path): - if self._zip_file is not None: - raise NotInstalledException - - raise NotImplementedError - - def _uninstall(self, install_path): - if not os.path.isdir(install_path): - raise InvalidPathException - if self._unzipped_extension is not None: - (name_, ext) = os.path.splitext(install_path) - if ext != self._unzipped_extension: - raise InvalidPathException - - for root, dirs, files in os.walk(install_path, topdown=False): - for name in files: - os.remove(os.path.join(root, name)) - for name in dirs: - path = os.path.join(root, name) - if os.path.islink(path): - os.remove(path) - else: - os.rmdir(path) - os.rmdir(install_path) diff --git a/src/sugar/bundle/contentbundle.py b/src/sugar/bundle/contentbundle.py deleted file mode 100644 index a95ed61..0000000 --- a/src/sugar/bundle/contentbundle.py +++ /dev/null @@ -1,220 +0,0 @@ -# Copyright (C) 2007, Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -"""Sugar content bundles - -UNSTABLE. -""" - -from ConfigParser import ConfigParser -import os -import urllib - -from sugar import env -from sugar.bundle.bundle import Bundle, NotInstalledException, \ - MalformedBundleException - -class ContentBundle(Bundle): - """A Sugar content bundle - - See http://wiki.laptop.org/go/Content_bundles for details - """ - - MIME_TYPE = 'application/vnd.olpc-content' - - _zipped_extension = '.xol' - _unzipped_extension = None - _infodir = 'library' - - def __init__(self, path): - Bundle.__init__(self, path) - - self._locale = None - self._l10n = None - self._category = None - self._name = None - self._subcategory = None - self._category_class = None - self._category_icon = None - self._library_version = None - self._bundle_class = None - self._activity_start = None - - info_file = self.get_file('library/library.info') - if info_file is None: - raise MalformedBundleException('No library.info file') - self._parse_info(info_file) - - if (self.get_file('index.html') is None and - self.get_file('library/library.xml') is None): - raise MalformedBundleException( - 'Content bundle %s has neither index.html nor library.xml' % - self._path) - - def _parse_info(self, info_file): - cp = ConfigParser() - cp.readfp(info_file) - - section = 'Library' - - if cp.has_option(section, 'host_version'): - version = cp.get(section, 'host_version') - try: - if int(version) != 1: - raise MalformedBundleException( - 'Content bundle %s has unknown host_version number %s' % - (self._path, version)) - except ValueError: - raise MalformedBundleException( - 'Content bundle %s has invalid host_version number %s' % - (self._path, version)) - - if cp.has_option(section, 'name'): - self._name = cp.get(section, 'name') - else: - raise MalformedBundleException( - 'Content bundle %s does not specify a name' % self._path) - - if cp.has_option(section, 'library_version'): - version = cp.get(section, 'library_version') - try: - self._library_version = int(version) - except ValueError: - raise MalformedBundleException( - 'Content bundle %s has invalid version number %s' % - (self._path, version)) - - if cp.has_option(section, 'l10n'): - l10n = cp.get(section, 'l10n') - if l10n == 'true': - self._l10n = True - elif l10n == 'false': - self._l10n = False - else: - raise MalformedBundleException( - 'Content bundle %s has invalid l10n key "%s"' % - (self._path, l10n)) - else: - raise MalformedBundleException( - 'Content bundle %s does not specify if it is localized' % - self._path) - - if cp.has_option(section, 'locale'): - self._locale = cp.get(section, 'locale') - else: - raise MalformedBundleException( - 'Content bundle %s does not specify a locale' % self._path) - - if cp.has_option(section, 'category'): - self._category = cp.get(section, 'category') - else: - raise MalformedBundleException( - 'Content bundle %s does not specify a category' % self._path) - - if cp.has_option(section, 'category_icon'): - self._category_icon = cp.get(section, 'category_icon') - else: - self._category_icon = None - - if cp.has_option(section, 'category_class'): - self._category_class = cp.get(section, 'category_class') - else: - self._category_class = None - - if cp.has_option(section, 'subcategory'): - self._subcategory = cp.get(section, 'subcategory') - else: - self._subcategory = None - - if cp.has_option(section, 'bundle_class'): - self._bundle_class = cp.get(section, 'bundle_class') - else: - self._bundle_class = None - - if cp.has_option(section, 'activity_start'): - self._activity_start = cp.get(section, 'activity_start') - else: - self._activity_start = 'index.html' - - def get_name(self): - return self._name - - def get_library_version(self): - return self._library_version - - def get_l10n(self): - return self._l10n - - def get_locale(self): - return self._locale - - def get_category(self): - return self._category - - def get_category_icon(self): - return self._category_icon - - def get_category_class(self): - return self._category_class - - def get_subcategory(self): - return self._subcategory - - def get_bundle_class(self): - return self._bundle_class - - def get_activity_start(self): - return self._activity_start - - def _run_indexer(self): - xdg_data_dirs = os.getenv('XDG_DATA_DIRS', - '/usr/local/share/:/usr/share/') - for path in xdg_data_dirs.split(':'): - indexer = os.path.join(path, 'library-common', 'make_index.py') - if os.path.exists(indexer): - os.spawnlp(os.P_WAIT, 'python', 'python', indexer) - - def get_root_dir(self): - return os.path.join(env.get_user_library_path(), self._zip_root_dir) - - def get_start_path(self): - return os.path.join(self.get_root_dir(), self._activity_start) - - def get_start_uri(self): - return "file://" + urllib.pathname2url(self.get_start_path()) - - def is_installed(self): - if self._zip_file is None: - return True - elif os.path.isdir(self.get_root_dir()): - return True - else: - return False - - def install(self): - self._unzip(env.get_user_library_path()) - self._run_indexer() - - def uninstall(self): - if self._zip_file is None: - if not self.is_installed(): - raise NotInstalledException - install_dir = self._path - else: - install_dir = os.path.join(self.get_root_dir()) - self._uninstall(install_dir) - self._run_indexer() -- cgit v0.9.1