From 6f324e9f859319778c201951051685d376c8e4db Mon Sep 17 00:00:00 2001 From: Ariel Calzada Date: Thu, 03 Jan 2013 18:12:34 +0000 Subject: Added proxy network support --- diff --git a/webactivity.py b/webactivity.py index 3c0d337..c5f13c1 100644 --- a/webactivity.py +++ b/webactivity.py @@ -159,6 +159,11 @@ class WebActivity(activity.Activity): session.set_property('ssl-use-system-ca-file', True) session.set_property('ssl-strict', False) + # Honor the http_proxy variable + if os.environ.get('http_proxy') is not None: + proxy_uri = Soup.URI.new(os.environ['http_proxy']) + session.set_property("proxy-uri",proxy_uri) + # By default, cookies are not stored persistently, we have to # add a cookie jar so that they get saved to disk. We use one # with a SQlite database: diff --git a/webactivity.py.orig b/webactivity.py.orig new file mode 100644 index 0000000..3c0d337 --- /dev/null +++ b/webactivity.py.orig @@ -0,0 +1,669 @@ +# Copyright (C) 2006, Red Hat, Inc. +# Copyright (C) 2009 Martin Langhoff, Simon Schampijer, Daniel Drake, +# Tomeu Vizoso +# +# 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 + +import logging +from gettext import gettext as _ +from gettext import ngettext +import os + +from gi.repository import GObject +GObject.threads_init() + +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GdkPixbuf +from gi.repository import WebKit +from gi.repository import Soup +from gi.repository import SoupGNOME + +import base64 +import time +import shutil +import sqlite3 +import json +from gi.repository import GConf +import locale +import cairo +import StringIO +from hashlib import sha1 + +from sugar3.activity import activity +from sugar3.graphics import style +import telepathy +import telepathy.client +from sugar3.presence import presenceservice +from sugar3.graphics.tray import HTray +from sugar3 import profile +from sugar3.graphics.alert import Alert +from sugar3.graphics.icon import Icon +from sugar3 import mime + +from sugar3.graphics.toolbarbox import ToolbarButton + +PROFILE_VERSION = 2 + +_profile_version = 0 +_profile_path = os.path.join(activity.get_activity_root(), 'data/gecko') +_version_file = os.path.join(_profile_path, 'version') +_cookies_db_path = os.path.join(_profile_path, 'cookies.sqlite') + +if os.path.exists(_version_file): + f = open(_version_file) + _profile_version = int(f.read()) + f.close() + +if _profile_version < PROFILE_VERSION: + if not os.path.exists(_profile_path): + os.mkdir(_profile_path) + + shutil.copy('cert8.db', _profile_path) + os.chmod(os.path.join(_profile_path, 'cert8.db'), 0660) + + f = open(_version_file, 'w') + f.write(str(PROFILE_VERSION)) + f.close() + + +def _seed_xs_cookie(cookie_jar): + """Create a HTTP Cookie to authenticate with the Schoolserver. + + Do nothing if the laptop is not registered with Schoolserver, or + if the cookie already exists. + + """ + client = GConf.Client.get_default() + backup_url = client.get_string('/desktop/sugar/backup_url') + if backup_url == '': + _logger.debug('seed_xs_cookie: Not registered with Schoolserver') + return + + jabber_server = client.get_string( + '/desktop/sugar/collaboration/jabber_server') + + soup_uri = Soup.URI() + soup_uri.set_scheme('xmpp') + soup_uri.set_host(jabber_server) + soup_uri.set_path('/') + xs_cookie = cookie_jar.get_cookies(soup_uri, for_http=False) + if xs_cookie is not None: + _logger.debug('seed_xs_cookie: Cookie exists already') + return + + pubkey = profile.get_profile().pubkey + cookie_data = {'color': profile.get_color().to_string(), + 'pkey_hash': sha1(pubkey).hexdigest()} + + expire = int(time.time()) + 10 * 365 * 24 * 60 * 60 + + xs_cookie = Soup.Cookie() + xs_cookie.set_name('xoid') + xs_cookie.set_value(json.dumps(cookie_data)) + xs_cookie.set_domain(jabber_server) + xs_cookie.set_path('/') + xs_cookie.set_max_age(expire) + cookie_jar.add_cookie(xs_cookie) + _logger.debug('seed_xs_cookie: Updated cookie successfully') + + +def _set_char_preference(name, value): + cls = components.classes["@mozilla.org/preferences-service;1"] + prefService = cls.getService(components.interfaces.nsIPrefService) + branch = prefService.getBranch('') + branch.setCharPref(name, value) + + +from browser import TabbedView +from browser import ZOOM_ORIGINAL +from webtoolbar import PrimaryToolbar +from edittoolbar import EditToolbar +from viewtoolbar import ViewToolbar +import downloadmanager + +# TODO: make the registration clearer SL #3087 + +from model import Model +from sugar3.presence.tubeconn import TubeConnection +from messenger import Messenger +from linkbutton import LinkButton + +SERVICE = "org.laptop.WebActivity" +IFACE = SERVICE +PATH = "/org/laptop/WebActivity" + +_logger = logging.getLogger('web-activity') + + +class WebActivity(activity.Activity): + def __init__(self, handle): + activity.Activity.__init__(self, handle) + + _logger.debug('Starting the web activity') + + session = WebKit.get_default_session() + session.set_property('accept-language-auto', True) + session.set_property('ssl-use-system-ca-file', True) + session.set_property('ssl-strict', False) + + # By default, cookies are not stored persistently, we have to + # add a cookie jar so that they get saved to disk. We use one + # with a SQlite database: + cookie_jar = SoupGNOME.CookieJarSqlite(filename=_cookies_db_path, + read_only=False) + session.add_feature(cookie_jar) + + _seed_xs_cookie(cookie_jar) + + # FIXME + # downloadmanager.remove_old_parts() + + self._force_close = False + self._tabbed_view = TabbedView() + self._tabbed_view.connect('focus-url-entry', self._on_focus_url_entry) + self._tabbed_view.connect('switch-page', self.__switch_page_cb) + + self._tray = HTray() + self.set_tray(self._tray, Gtk.PositionType.BOTTOM) + + self._primary_toolbar = PrimaryToolbar(self._tabbed_view, self) + self._edit_toolbar = EditToolbar(self) + self._view_toolbar = ViewToolbar(self) + + self._primary_toolbar.connect('add-link', self._link_add_button_cb) + + self._primary_toolbar.connect('go-home', self._go_home_button_cb) + + self._edit_toolbar_button = ToolbarButton( + page=self._edit_toolbar, + icon_name='toolbar-edit') + + self._primary_toolbar.toolbar.insert( + self._edit_toolbar_button, 1) + + view_toolbar_button = ToolbarButton( + page=self._view_toolbar, + icon_name='toolbar-view') + self._primary_toolbar.toolbar.insert( + view_toolbar_button, 2) + + self._primary_toolbar.show_all() + self.set_toolbar_box(self._primary_toolbar) + + self.set_canvas(self._tabbed_view) + self._tabbed_view.show() + + self.model = Model() + self.model.connect('add_link', self._add_link_model_cb) + + self.connect('key-press-event', self._key_press_cb) + + if handle.uri: + self._tabbed_view.current_browser.load_uri(handle.uri) + elif not self._jobject.file_path: + # TODO: we need this hack until we extend the activity API for + # opening URIs and default docs. + self._tabbed_view.load_homepage() + + self.messenger = None + self.connect('shared', self._shared_cb) + + # Get the Presence Service + self.pservice = presenceservice.get_instance() + try: + name, path = self.pservice.get_preferred_connection() + self.tp_conn_name = name + self.tp_conn_path = path + self.conn = telepathy.client.Connection(name, path) + except TypeError: + _logger.debug('Offline') + self.initiating = None + + if self.get_shared_activity() is not None: + _logger.debug('shared: %s', self.get_shared()) + # We are joining the activity + _logger.debug('Joined activity') + self.connect('joined', self._joined_cb) + if self.get_shared(): + # We've already joined + self._joined_cb() + else: + _logger.debug('Created activity') + + # README: this is a workaround to remove old temp file + # http://bugs.sugarlabs.org/ticket/3973 + self._cleanup_temp_files() + + def _cleanup_temp_files(self): + """Removes temporary files generated by Download Manager that + were cancelled by the user or failed for any reason. + + There is a bug in GLib that makes this to happen: + https://bugzilla.gnome.org/show_bug.cgi?id=629301 + """ + + try: + uptime_proc = open('/proc/uptime', 'r').read() + uptime = int(float(uptime_proc.split()[0])) + except EnvironmentError: + logging.warning('/proc/uptime could not be read') + uptime = None + + temp_path = os.path.join(self.get_activity_root(), 'instance') + now = int(time.time()) + cutoff = now - 24 * 60 * 60 # yesterday + if uptime is not None: + boot_time = now - uptime + cutoff = max(cutoff, boot_time) + + for f in os.listdir(temp_path): + if f.startswith('.goutputstream-'): + fpath = os.path.join(temp_path, f) + mtime = int(os.path.getmtime(fpath)) + if mtime < cutoff: + logging.warning('Removing old temporary file: %s', fpath) + try: + os.remove(fpath) + except EnvironmentError: + logging.error('Temporary file could not be ' + 'removed: %s', fpath) + + def _on_focus_url_entry(self, gobject): + self._primary_toolbar.entry.grab_focus() + + def _shared_cb(self, activity_): + _logger.debug('My activity was shared') + self.initiating = True + self._setup() + + _logger.debug('This is my activity: making a tube...') + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(SERVICE, + {}) + + def _setup(self): + if self.get_shared_activity() is None: + _logger.debug('Failed to share or join activity') + return + + bus_name, conn_path, channel_paths = \ + self.get_shared_activity().get_channels() + + # Work out what our room is called and whether we have Tubes already + room = None + tubes_chan = None + text_chan = None + for channel_path in channel_paths: + channel = telepathy.client.Channel(bus_name, channel_path) + htype, handle = channel.GetHandle() + if htype == telepathy.HANDLE_TYPE_ROOM: + _logger.debug('Found our room: it has handle#%d "%s"', + handle, + self.conn.InspectHandles(htype, [handle])[0]) + room = handle + ctype = channel.GetChannelType() + if ctype == telepathy.CHANNEL_TYPE_TUBES: + _logger.debug('Found our Tubes channel at %s', + channel_path) + tubes_chan = channel + elif ctype == telepathy.CHANNEL_TYPE_TEXT: + _logger.debug('Found our Text channel at %s', + channel_path) + text_chan = channel + + if room is None: + _logger.debug("Presence service didn't create a room") + return + if text_chan is None: + _logger.debug("Presence service didn't create a text channel") + return + + # Make sure we have a Tubes channel - PS doesn't yet provide one + if tubes_chan is None: + _logger.debug("Didn't find our Tubes channel, requesting one...") + tubes_chan = self.conn.request_channel( + telepathy.CHANNEL_TYPE_TUBES, telepathy.HANDLE_TYPE_ROOM, + room, True) + + self.tubes_chan = tubes_chan + self.text_chan = text_chan + + tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( \ + 'NewTube', self._new_tube_cb) + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + _logger.debug('ListTubes() failed: %s', e) + + def _joined_cb(self, activity_): + if not self.get_shared_activity(): + return + + _logger.debug('Joined an existing shared activity') + + self.initiating = False + self._setup() + + _logger.debug('This is not my activity: waiting for a tube...') + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + def _new_tube_cb(self, identifier, initiator, type, service, params, + state): + _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', identifier, initiator, type, + service, params, state) + + if (type == telepathy.TUBE_TYPE_DBUS and + service == SERVICE): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube( + identifier) + + self.tube_conn = TubeConnection(self.conn, + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], + identifier, group_iface=self.text_chan[ + telepathy.CHANNEL_INTERFACE_GROUP]) + + _logger.debug('Tube created') + self.messenger = Messenger(self.tube_conn, self.initiating, + self.model) + + def _get_data_from_file_path(self, file_path): + fd = open(file_path, 'r') + try: + data = fd.read() + finally: + fd.close() + return data + + def read_file(self, file_path): + if self.metadata['mime_type'] == 'text/plain': + data = self._get_data_from_file_path(file_path) + self.model.deserialize(data) + + for link in self.model.data['shared_links']: + _logger.debug('read: url=%s title=%s d=%s' % (link['url'], + link['title'], + link['color'])) + self._add_link_totray(link['url'], + base64.b64decode(link['thumb']), + link['color'], link['title'], + link['owner'], -1, link['hash']) + logging.debug('########## reading %s', data) + self._tabbed_view.set_history(self.model.data['history']) + for number, tab in enumerate(self.model.data['currents']): + tab_page = self._tabbed_view.get_nth_page(number) + tab_page.browser.set_history_index(tab['history_index']) + tab_page.browser.grab_focus() + + self._tabbed_view.set_current_page(self.model.data['current_tab']) + elif self.metadata['mime_type'] == 'text/uri-list': + data = self._get_data_from_file_path(file_path) + uris = mime.split_uri_list(data) + if len(uris) == 1: + self._tabbed_view.props.current_browser.load_uri(uris[0]) + else: + _logger.error('Open uri-list: Does not support' + 'list of multiple uris by now.') + else: + file_uri = 'file://' + file_path + self._tabbed_view.props.current_browser.load_uri(file_uri) + self._tabbed_view.props.current_browser.grab_focus() + + def write_file(self, file_path): + if not self.metadata['mime_type']: + self.metadata['mime_type'] = 'text/plain' + + if self.metadata['mime_type'] == 'text/plain': + + browser = self._tabbed_view.current_browser + + if not self._jobject.metadata['title_set_by_user'] == '1': + if browser.props.title is None: + self.metadata['title'] = _('Untitled') + else: + self.metadata['title'] = browser.props.title + + self.model.data['history'] = self._tabbed_view.get_history() + current_tab = self._tabbed_view.get_current_page() + self.model.data['current_tab'] = current_tab + + self.model.data['currents'] = [] + for n in range(0, self._tabbed_view.get_n_pages()): + tab_page = self._tabbed_view.get_nth_page(n) + n_browser = tab_page.browser + if n_browser != None: + uri = n_browser.get_uri() + history_index = n_browser.get_history_index() + info = {'title': n_browser.props.title, 'url': uri, + 'history_index': history_index} + + self.model.data['currents'].append(info) + + f = open(file_path, 'w') + try: + logging.debug('########## writing %s', self.model.serialize()) + f.write(self.model.serialize()) + finally: + f.close() + + def _link_add_button_cb(self, button): + self._add_link() + + def _go_home_button_cb(self, button): + self._tabbed_view.load_homepage() + + def _key_press_cb(self, widget, event): + key_name = Gdk.keyval_name(event.keyval) + browser = self._tabbed_view.props.current_browser + + if event.get_state() & Gdk.ModifierType.CONTROL_MASK: + + if key_name == 'd': + self._add_link() + elif key_name == 'f': + _logger.debug('keyboard: Find') + self._edit_toolbar_button.set_expanded(True) + self._edit_toolbar.search_entry.grab_focus() + elif key_name == 'l': + _logger.debug('keyboard: Focus url entry') + self._primary_toolbar.entry.grab_focus() + elif key_name == 'minus': + _logger.debug('keyboard: Zoom out') + browser.zoom_out() + elif key_name in ['plus', 'equal']: + _logger.debug('keyboard: Zoom in') + browser.zoom_in() + elif key_name == '0': + _logger.debug('keyboard: Actual size') + browser.set_zoom_level(ZOOM_ORIGINAL) + elif key_name == 'Left': + _logger.debug('keyboard: Go back') + browser.go_back() + elif key_name == 'Right': + _logger.debug('keyboard: Go forward') + browser.go_forward() + elif key_name == 'r': + _logger.debug('keyboard: Reload') + browser.reload() + elif Gdk.keyval_name(event.keyval) == "t": + self._tabbed_view.add_tab() + else: + return False + + return True + + elif key_name in ('KP_Up', 'KP_Down', 'KP_Left', 'KP_Right'): + scrolled_window = browser.get_parent() + + if key_name in ('KP_Up', 'KP_Down'): + adjustment = scrolled_window.get_vadjustment() + elif key_name in ('KP_Left', 'KP_Right'): + adjustment = scrolled_window.get_hadjustment() + value = adjustment.get_value() + step = adjustment.get_step_increment() + + if key_name in ('KP_Up', 'KP_Left'): + adjustment.set_value(value - step) + elif key_name in ('KP_Down', 'KP_Right'): + adjustment.set_value(value + step) + + return True + + elif key_name == 'Escape': + status = browser.get_load_status() + loading = WebKit.LoadStatus.PROVISIONAL <= status \ + < WebKit.LoadStatus.FINISHED + if loading: + _logger.debug('keyboard: Stop loading') + browser.stop_loading() + + return False + + def _add_link(self): + ''' take screenshot and add link info to the model ''' + + browser = self._tabbed_view.props.current_browser + ui_uri = browser.get_uri() + + for link in self.model.data['shared_links']: + if link['hash'] == sha1(ui_uri).hexdigest(): + _logger.debug('_add_link: link exist already a=%s b=%s', + link['hash'], sha1(ui_uri).hexdigest()) + return + buf = self._get_screenshot() + timestamp = time.time() + self.model.add_link(ui_uri, browser.props.title, buf, + profile.get_nick_name(), + profile.get_color().to_string(), timestamp) + + if self.messenger is not None: + self.messenger._add_link(ui_uri, browser.props.title, + profile.get_color().to_string(), + profile.get_nick_name(), + base64.b64encode(buf), timestamp) + + def _add_link_model_cb(self, model, index): + ''' receive index of new link from the model ''' + link = self.model.data['shared_links'][index] + self._add_link_totray(link['url'], base64.b64decode(link['thumb']), + link['color'], link['title'], + link['owner'], index, link['hash']) + + def _add_link_totray(self, url, buf, color, title, owner, index, hash): + ''' add a link to the tray ''' + item = LinkButton(buf, color, title, owner, hash) + item.connect('clicked', self._link_clicked_cb, url) + item.connect('remove_link', self._link_removed_cb) + # use index to add to the tray + self._tray.add_item(item, index) + item.show() + self._view_toolbar.traybutton.props.sensitive = True + self._view_toolbar.traybutton.props.active = True + + def _link_removed_cb(self, button, hash): + ''' remove a link from tray and delete it in the model ''' + self.model.remove_link(hash) + self._tray.remove_item(button) + if len(self._tray.get_children()) == 0: + self._view_toolbar.traybutton.props.sensitive = False + self._view_toolbar.traybutton.props.active = False + + def _link_clicked_cb(self, button, url): + ''' an item of the link tray has been clicked ''' + self._tabbed_view.props.current_browser.load_uri(url) + + def _get_screenshot(self): + browser = self._tabbed_view.props.current_browser + window = browser.get_window() + width, height = window.get_width(), window.get_height() + + thumb_width, thumb_height = style.zoom(100), style.zoom(80) + + thumb_surface = Gdk.Window.create_similar_surface(window, + cairo.CONTENT_COLOR, thumb_width, thumb_height) + + cairo_context = cairo.Context(thumb_surface) + thumb_scale_w = thumb_width * 1.0 / width + thumb_scale_h = thumb_height * 1.0 / height + cairo_context.scale(thumb_scale_w, thumb_scale_h) + Gdk.cairo_set_source_window(cairo_context, window, 0, 0) + cairo_context.paint() + + thumb_str = StringIO.StringIO() + thumb_surface.write_to_png(thumb_str) + return thumb_str.getvalue() + + def can_close(self): + if self._force_close: + return True + elif downloadmanager.can_quit(): + return True + else: + alert = Alert() + alert.props.title = ngettext('Download in progress', + 'Downloads in progress', + downloadmanager.num_downloads()) + message = ngettext('Stopping now will erase your download', + 'Stopping now will erase your downloads', + downloadmanager.num_downloads()) + alert.props.msg = message + cancel_icon = Icon(icon_name='dialog-cancel') + cancel_label = ngettext('Continue download', 'Continue downloads', + downloadmanager.num_downloads()) + alert.add_button(Gtk.ResponseType.CANCEL, cancel_label, + cancel_icon) + stop_icon = Icon(icon_name='dialog-ok') + alert.add_button(Gtk.ResponseType.OK, _('Stop'), stop_icon) + stop_icon.show() + self.add_alert(alert) + alert.connect('response', self.__inprogress_response_cb) + alert.show() + self.present() + return False + + def __inprogress_response_cb(self, alert, response_id): + self.remove_alert(alert) + if response_id is Gtk.ResponseType.CANCEL: + logging.debug('Keep on') + elif response_id == Gtk.ResponseType.OK: + logging.debug('Stop downloads and quit') + self._force_close = True + downloadmanager.remove_all_downloads() + self.close() + + def __switch_page_cb(self, tabbed_view, page, page_num): + browser = page._browser + status = browser.get_load_status() + + if status in (WebKit.LoadStatus.COMMITTED, + WebKit.LoadStatus.FIRST_VISUALLY_NON_EMPTY_LAYOUT): + self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) + elif status in (WebKit.LoadStatus.PROVISIONAL, + WebKit.LoadStatus.FAILED, + WebKit.LoadStatus.FINISHED): + self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR)) + + def get_document_path(self, async_cb, async_err_cb): + browser = self._tabbed_view.props.current_browser + browser.get_source(async_cb, async_err_cb) + + def get_canvas(self): + return self._tabbed_view -- cgit v0.9.1