#!/usr/bin/python # Copyright (C) 2009, Sugar Labs # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA '''Activity information microformat parser. Activity information is embedded in HTML/XHTML/XML pages using a Resource Description Framework (RDF) http://www.w3.org/RDF/ . An example:: 7 {3ca105e0-2280-4897-99a0-c277d1b733d2} 0.82 0.84 http://foo.xo 7 sha256:816a7c43b4f1ea4769c61c03ea4.. ''' import logging from xml.etree.ElementTree import XML import traceback import gio from jarabe import config _FIND_DESCRIPTION = \ './/{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description' _FIND_VERSION = './/{http://www.mozilla.org/2004/em-rdf#}version' _FIND_LINK = './/{http://www.mozilla.org/2004/em-rdf#}updateLink' _FIND_SIZE = './/{http://www.mozilla.org/2004/em-rdf#}updateSize' _UPDATE_PATH = 'http://activities.sugarlabs.org/services/update.php' _fetcher = None class _UpdateFetcher(object): _CHUNK_SIZE = 10240 def __init__(self, bundle, completion_cb): url = '%s?id=%s&appVersion=%s' % \ (_UPDATE_PATH, bundle.get_bundle_id(), config.version) self._completion_cb = completion_cb self._file = gio.File(url) self._stream = None self._xml_data = '' self._bundle = bundle self._file.read_async(self.__file_read_async_cb) def __file_read_async_cb(self, gfile, result): try: self._stream = self._file.read_finish(result) except: global _fetcher _fetcher = None self._completion_cb(None, None, None, None, traceback.format_exc()) return self._stream.read_async(self._CHUNK_SIZE, self.__stream_read_async_cb) def __stream_read_async_cb(self, stream, result): xml_data = self._stream.read_finish(result) if xml_data is None: global _fetcher _fetcher = None self._completion_cb(self._bundle, None, None, None, 'Error reading update information for %s from ' 'server.' % self._bundle.get_bundle_id()) return elif not xml_data: self._process_result() else: self._xml_data += xml_data self._stream.read_async(self._CHUNK_SIZE, self.__stream_read_async_cb) def _process_result(self): document = XML(self._xml_data) if document.find(_FIND_DESCRIPTION) is None: logging.debug('Bundle %s not available in the server for the ' 'version %s', self._bundle.get_bundle_id(), config.version) version = None link = None size = None else: try: version = int(document.find(_FIND_VERSION).text) except ValueError: logging.error(traceback.format_exc()) version = 0 link = document.find(_FIND_LINK).text try: size = long(document.find(_FIND_SIZE).text) * 1024 except ValueError: logging.error(traceback.format_exc()) size = 0 global _fetcher _fetcher = None self._completion_cb(self._bundle, version, link, size, None) def fetch_update_info(bundle, completion_cb): '''Queries the server for a newer version of the ActivityBundle. completion_cb receives bundle, version, link, size and possibly an error message: def completion_cb(bundle, version, link, size, error_message): ''' global _fetcher if _fetcher is not None: raise RuntimeError('Multiple simultaneous requests are not supported') _fetcher = _UpdateFetcher(bundle, completion_cb)