From 35035af44de1f7929cf02cd165f82c93a98c9575 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Sat, 23 Jan 2010 15:39:03 +0000 Subject: Implement support for 3G modems (tch) #1622 --- diff --git a/configure.ac b/configure.ac index f0db28e..6231d76 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,7 @@ extensions/cpsection/datetime/Makefile extensions/cpsection/frame/Makefile extensions/cpsection/keyboard/Makefile extensions/cpsection/language/Makefile +extensions/cpsection/modemconfiguration/Makefile extensions/cpsection/Makefile extensions/cpsection/network/Makefile extensions/cpsection/power/Makefile diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am index d2f4ede..a35643a 100644 --- a/data/icons/Makefile.am +++ b/data/icons/Makefile.am @@ -7,6 +7,7 @@ sugar_DATA = \ module-frame.svg \ module-keyboard.svg \ module-language.svg \ + module-modemconfiguration.svg \ module-network.svg \ module-power.svg \ module-updater.svg diff --git a/data/icons/module-modemconfiguration.svg b/data/icons/module-modemconfiguration.svg new file mode 100644 index 0000000..b62a12e --- /dev/null +++ b/data/icons/module-modemconfiguration.svg @@ -0,0 +1,1272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Workgroup + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + lan + workgroup + network + peer + + + + + Garrett LeSage + + + + + + + + + + + + + + + + + + + + + + + www + + diff --git a/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am index dd0a6b8..a92b5dd 100644 --- a/extensions/cpsection/Makefile.am +++ b/extensions/cpsection/Makefile.am @@ -1,4 +1,5 @@ -SUBDIRS = aboutme aboutcomputer datetime frame keyboard language network power updater +SUBDIRS = aboutme aboutcomputer datetime frame keyboard language \ + modemconfiguration network power updater sugardir = $(pkgdatadir)/extensions/cpsection sugar_PYTHON = __init__.py diff --git a/extensions/cpsection/modemconfiguration/Makefile.am b/extensions/cpsection/modemconfiguration/Makefile.am new file mode 100644 index 0000000..3e2613e --- /dev/null +++ b/extensions/cpsection/modemconfiguration/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pkgdatadir)/extensions/cpsection/modemconfiguration + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py diff --git a/extensions/cpsection/modemconfiguration/__init__.py b/extensions/cpsection/modemconfiguration/__init__.py new file mode 100644 index 0000000..8a219dc --- /dev/null +++ b/extensions/cpsection/modemconfiguration/__init__.py @@ -0,0 +1,22 @@ +# Copyright (C) 2009 Paraguay Educa, Martin Abente +# +# 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 US + +from gettext import gettext as _ + +CLASS = 'ModemConfiguration' +ICON = 'module-modemconfiguration' +TITLE = _('Modem Configuration') + diff --git a/extensions/cpsection/modemconfiguration/model.py b/extensions/cpsection/modemconfiguration/model.py new file mode 100644 index 0000000..f96e88f --- /dev/null +++ b/extensions/cpsection/modemconfiguration/model.py @@ -0,0 +1,53 @@ +# Copyright (C) 2009 Paraguay Educa, Martin Abente +# +# 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 US + +import gconf + +from jarabe.model.network import GSM_USERNAME_PATH, GSM_PASSWORD_PATH, \ + GSM_NUMBER_PATH, GSM_APN_PATH + +def get_username(): + client = gconf.client_get_default() + return client.get_string(GSM_USERNAME_PATH) or '' + +def get_password(): + client = gconf.client_get_default() + return client.get_string(GSM_PASSWORD_PATH) or '' + +def get_number(): + client = gconf.client_get_default() + return client.get_string(GSM_NUMBER_PATH) or '' + +def get_apn(): + client = gconf.client_get_default() + return client.get_string(GSM_APN_PATH) or '' + +def set_username(username): + client = gconf.client_get_default() + client.set_string(GSM_USERNAME_PATH, username) + +def set_password(password): + client = gconf.client_get_default() + client.set_string(GSM_PASSWORD_PATH, password) + +def set_number(number): + client = gconf.client_get_default() + client.set_string(GSM_NUMBER_PATH, number) + +def set_apn(apn): + client = gconf.client_get_default() + client.set_string(GSM_APN_PATH, apn) + diff --git a/extensions/cpsection/modemconfiguration/view.py b/extensions/cpsection/modemconfiguration/view.py new file mode 100644 index 0000000..d66f1d5 --- /dev/null +++ b/extensions/cpsection/modemconfiguration/view.py @@ -0,0 +1,192 @@ +# Copyright (C) 2009 Paraguay Educa, Martin Abente +# +# 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 US + +import os +import logging +from gettext import gettext as _ + +import gtk +import gobject + +from sugar.graphics import style + +from jarabe.controlpanel.sectionview import SectionView + +APPLY_TIMEOUT = 1000 + +class EntryWithLabel(gtk.HBox): + __gtype_name__ = "SugarEntryWithLabel" + + def __init__(self, label_text): + gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING) + + self._timeout_sid = 0 + self._changed_handler = None + self._is_valid = True + + label = gtk.Label(label_text) + label.modify_fg(gtk.STATE_NORMAL, + style.COLOR_SELECTION_GREY.get_gdk_color()) + self.pack_start(label, True, True) + label.show() + + self._entry = gtk.Entry(25) + self._entry.connect('changed', self.__entry_changed_cb) + self._entry.set_width_chars(25) + self.pack_start(self._entry, expand=False) + self._entry.show() + + def __entry_changed_cb(self, widget, data=None): + if self._timeout_sid: + gobject.source_remove(self._timeout_sid) + self._timeout_sid = gobject.timeout_add(APPLY_TIMEOUT, + self.__timeout_cb) + + def __timeout_cb(self): + self._timeout_sid = 0 + + if self._entry.get_text() == self.get_value(): + return False + + try: + self.set_value(self._entry.get_text()) + except ValueError: + self._is_valid = False + else: + self._is_valid = True + + self.notify('is-valid') + + return False + + def set_text_from_model(self): + self._entry.set_text(self.get_value()) + + def get_value(self): + raise NotImplementedError + + def set_value(self): + raise NotImplementedError + + def _get_is_valid(self): + return self._is_valid + is_valid = gobject.property(type=bool, getter=_get_is_valid, default=True) + +class UsernameEntry(EntryWithLabel): + def __init__(self, model): + EntryWithLabel.__init__(self, _('Username:')) + self._model = model + + def get_value(self): + return self._model.get_username() + + def set_value(self, username): + return self._model.set_username(username) + +class PasswordEntry(EntryWithLabel): + def __init__(self, model): + EntryWithLabel.__init__(self, _('Password:')) + self._model = model + + def get_value(self): + return self._model.get_password() + + def set_value(self, password): + return self._model.set_password(password) + +class NumberEntry(EntryWithLabel): + def __init__(self, model): + EntryWithLabel.__init__(self, _('Number:')) + self._model = model + + def get_value(self): + return self._model.get_number() + + def set_value(self, number): + return self._model.set_number(number) + +class ApnEntry(EntryWithLabel): + def __init__(self, model): + EntryWithLabel.__init__(self, _('APN:')) + self._model = model + + def get_value(self): + return self._model.get_apn() + + def set_value(self, apn): + return self._model.set_apn(apn) + +class ModemConfiguration(SectionView): + def __init__(self, model, alerts=None): + SectionView.__init__(self) + + self._model = model + self.restart_alerts = alerts + + self.set_border_width(style.DEFAULT_SPACING) + self.set_spacing(style.DEFAULT_SPACING) + + self._username_entry = UsernameEntry(model) + self._username_entry.connect('notify::is-valid', + self.__notify_is_valid_cb) + self.pack_start(self._username_entry, expand=False) + self._username_entry.show() + + self._password_entry = PasswordEntry(model) + self._password_entry.connect('notify::is-valid', + self.__notify_is_valid_cb) + self.pack_start(self._password_entry, expand=False) + self._password_entry.show() + + self._number_entry = NumberEntry(model) + self._number_entry.connect('notify::is-valid', + self.__notify_is_valid_cb) + self.pack_start(self._number_entry, expand=False) + self._number_entry.show() + + self._apn_entry = ApnEntry(model) + self._apn_entry.connect('notify::is-valid', + self.__notify_is_valid_cb) + self.pack_start(self._apn_entry, expand=False) + self._apn_entry.show() + + self.setup() + + def setup(self): + self._username_entry.set_text_from_model() + self._password_entry.set_text_from_model() + self._number_entry.set_text_from_model() + self._apn_entry.set_text_from_model() + + self.needs_restart = False + + def undo(self): + self._model.undo() + + def _validate(self): + if self._username_entry.is_valid and \ + self._password_entry.is_valid and \ + self._number_entry.is_valid and \ + self._apn_entry.is_valid: + self.props.is_valid = True + else: + self.props.is_valid = False + + def __notify_is_valid_cb(self, entry, pspec): + if entry.is_valid: + self.needs_restart = True + self._validate() + diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py index e40e196..2ca6a88 100644 --- a/extensions/deviceicon/network.py +++ b/extensions/deviceicon/network.py @@ -1,6 +1,7 @@ # # Copyright (C) 2008 One Laptop Per Child # Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer +# Copyright (C) 2009 Paraguay Educa, Martin Abente # # 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 @@ -55,16 +56,10 @@ _NM_OLPC_MESH_IFACE = 'org.freedesktop.NetworkManager.Device.OlpcMesh' _NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint' _NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active' -_NM_DEVICE_STATE_UNKNOWN = 0 -_NM_DEVICE_STATE_UNMANAGED = 1 -_NM_DEVICE_STATE_UNAVAILABLE = 2 -_NM_DEVICE_STATE_DISCONNECTED = 3 -_NM_DEVICE_STATE_PREPARE = 4 -_NM_DEVICE_STATE_CONFIG = 5 -_NM_DEVICE_STATE_NEED_AUTH = 6 -_NM_DEVICE_STATE_IP_CONFIG = 7 -_NM_DEVICE_STATE_ACTIVATED = 8 -_NM_DEVICE_STATE_FAILED = 9 +_GSM_STATE_NOT_READY = 0 +_GSM_STATE_DISCONNECTED = 1 +_GSM_STATE_CONNECTING = 2 +_GSM_STATE_CONNECTED = 3 def frequency_to_channel(frequency): ftoc = { 2412: 1, 2417: 2, 2422: 3, 2427: 4, @@ -215,6 +210,65 @@ class WiredPalette(Palette): ip_address_text = "" self._ip_address_label.set_text(ip_address_text) +class GsmPalette(Palette): + __gtype_name__ = 'SugarGsmPalette' + + __gsignals__ = { + 'gsm-connect' : (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])), + 'gsm-disconnect' : (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])), + } + + def __init__(self): + Palette.__init__(self, label=_('Wireless modem')) + + self._current_state = None + + self._toggle_state_item = gtk.MenuItem('') + self._toggle_state_item.connect('activate', self.__toggle_state_cb) + self.menu.append(self._toggle_state_item) + self._toggle_state_item.show() + + self.set_state(_GSM_STATE_NOT_READY) + + def set_state(self, state): + self._current_state = state + self._update_label_and_text() + + def _update_label_and_text(self): + if self._current_state == _GSM_STATE_NOT_READY: + self._toggle_state_item.get_child().set_label('...') + self.props.secondary_text = _('Please wait...') + + elif self._current_state == _GSM_STATE_DISCONNECTED: + self._toggle_state_item.get_child().set_label(_('Connect')) + self.props.secondary_text = _('Disconnected') + + elif self._current_state == _GSM_STATE_CONNECTING: + self._toggle_state_item.get_child().set_label(_('Cancel')) + self.props.secondary_text = _('Connecting...') + + elif self._current_state == _GSM_STATE_CONNECTED: + self._toggle_state_item.get_child().set_label(_('Disconnect')) + self.props.secondary_text = _('Connected') + else: + raise ValueError('Invalid GSM state while updating label and ' \ + 'text, %s' % str(self._current_state)) + + def __toggle_state_cb(self, menuitem): + if self._current_state == _GSM_STATE_NOT_READY: + pass + elif self._current_state == _GSM_STATE_DISCONNECTED: + self.emit('gsm-connect') + elif self._current_state == _GSM_STATE_CONNECTING: + self.emit('gsm-disconnect') + elif self._current_state == _GSM_STATE_CONNECTED: + self.emit('gsm-disconnect') + else: + raise ValueError('Invalid GSM state while emitting signal, %s' % \ + str(self._current_state)) + class WirelessDeviceView(ToolButton): @@ -451,11 +505,11 @@ class WirelessDeviceView(ToolButton): connection_name = format % nick connection_name += color_suffix - connection = network.find_connection(connection_name) + connection = network.find_connection_by_ssid(connection_name) if connection is None: settings = Settings() settings.connection.id = 'Auto ' + connection_name - settings.connection.uuid = unique_id() + uuid = settings.connection.uuid = unique_id() settings.connection.type = '802-11-wireless' settings.wireless.ssid = dbus.ByteArray(connection_name) settings.wireless.band = 'bg' @@ -463,7 +517,7 @@ class WirelessDeviceView(ToolButton): settings.ip4_config = IP4Config() settings.ip4_config.method = 'link-local' - connection = network.add_connection(connection_name, settings) + connection = network.add_connection(uuid, settings) obj = self._bus.get_object(_NM_SERVICE, _NM_PATH) netmgr = dbus.Interface(obj, _NM_IFACE) @@ -623,6 +677,122 @@ class WiredDeviceView(TrayIcon): self._palette.set_connected(speed, address) +class GsmDeviceView(TrayIcon): + + _ICON_NAME = 'gsm-device' + FRAME_POSITION_RELATIVE = 303 + + def __init__(self, device): + client = gconf.client_get_default() + color = xocolor.XoColor(client.get_string('/desktop/sugar/user/color')) + + TrayIcon.__init__(self, icon_name=self._ICON_NAME, xo_color=color) + + self._bus = dbus.SystemBus() + self._device = device + self._palette = None + self.set_palette_invoker(FrameWidgetInvoker(self)) + + self._bus.add_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=_NM_DEVICE_IFACE) + + def create_palette(self): + palette = GsmPalette() + + palette.set_group_id('frame') + palette.connect('gsm-connect', self.__gsm_connect_cb) + palette.connect('gsm-disconnect', self.__gsm_disconnect_cb) + + self._palette = palette + + props = dbus.Interface(self._device, 'org.freedesktop.DBus.Properties') + props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True, + reply_handler=self.__current_state_check_cb, + error_handler=self.__current_state_check_error_cb) + + return palette + + def __gsm_connect_cb(self, palette, data=None): + connection = network.find_gsm_connection() + if connection is not None: + obj = self._bus.get_object(_NM_SERVICE, _NM_PATH) + netmgr = dbus.Interface(obj, _NM_IFACE) + netmgr.ActivateConnection(network.SETTINGS_SERVICE, + connection.path, + self._device.object_path, + '/', + reply_handler=self.__connect_cb, + error_handler=self.__connect_error_cb) + + def __connect_cb(self, active_connection): + logging.debug('Connected successfully to gsm device, %s', + active_connection) + + def __connect_error_cb(self, error): + raise RuntimeError('Error when connecting to gsm device, %s' % error) + + def __gsm_disconnect_cb(self, palette, data=None): + obj = self._bus.get_object(_NM_SERVICE, _NM_PATH) + netmgr = dbus.Interface(obj, _NM_IFACE) + netmgr_props = dbus.Interface(netmgr, 'org.freedesktop.DBus.Properties') + active_connections_o = netmgr_props.Get(_NM_IFACE, 'ActiveConnections') + + for conn_o in active_connections_o: + obj = self._bus.get_object(_NM_IFACE, conn_o) + props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties') + devices = props.Get(_NM_ACTIVE_CONN_IFACE, 'Devices') + if self._device.object_path in devices: + netmgr.DeactivateConnection( + conn_o, + reply_handler=self.__disconnect_cb, + error_handler=self.__disconnect_error_cb) + break + + def __disconnect_cb(self): + logging.debug('Disconnected successfully gsm device') + + def __disconnect_error_cb(self, error): + raise RuntimeError('Error when disconnecting gsm device, %s' % error) + + def __state_changed_cb(self, new_state, old_state, reason): + self._update_state(int(new_state)) + + def __current_state_check_cb(self, properties): + self._update_state(int(properties['State'])) + + def __current_state_check_error_cb(self, error): + raise RuntimeError('Error when checking gsm device state, %s' % error) + + def _update_state(self, state): + gsm_state = None + + if state is network.DEVICE_STATE_ACTIVATED: + gsm_state = _GSM_STATE_CONNECTED + + elif state is network.DEVICE_STATE_DISCONNECTED: + gsm_state = _GSM_STATE_DISCONNECTED + + elif state in [network.DEVICE_STATE_UNMANAGED, + network.DEVICE_STATE_UNAVAILABLE, + network.DEVICE_STATE_UNKNOWN]: + gsm_state = _GSM_STATE_NOT_READY + + elif state in [network.DEVICE_STATE_PREPARE, + network.DEVICE_STATE_CONFIG, + network.DEVICE_STATE_IP_CONFIG]: + gsm_state = _GSM_STATE_CONNECTING + + if self._palette is not None: + self._palette.set_state(gsm_state) + + def disconnect(self): + self._bus.remove_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=_NM_DEVICE_IFACE) + class WirelessDeviceObserver(object): def __init__(self, device, tray, device_type): self._device = device @@ -633,6 +803,8 @@ class WirelessDeviceObserver(object): self._device_view = WirelessDeviceView(self._device) elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH: self._device_view = OlpcMeshDeviceView(self._device) + else: + raise ValueError('Unimplemented device type %d' % device_type) self._tray.add_device(self._device_view) @@ -691,6 +863,19 @@ class WiredDeviceObserver(object): del self._device_view self._device_view = None +class GsmDeviceObserver(object): + def __init__(self, device, tray): + self._device = device + self._device_view = None + self._tray = tray + + self._device_view = GsmDeviceView(device) + self._tray.add_device(self._device_view) + + def disconnect(self): + self._device_view.disconnect() + self._tray.remove_device(self._device_view) + self._device_view = None class NetworkManagerObserver(object): def __init__(self, tray): @@ -735,6 +920,9 @@ class NetworkManagerObserver(object): network.DEVICE_TYPE_802_11_OLPC_MESH]: device = WirelessDeviceObserver(nm_device, self._tray, device_type) self._devices[device_op] = device + elif device_type == network.DEVICE_TYPE_GSM_MODEM: + device = GsmDeviceObserver(nm_device, self._tray) + self._devices[device_op] = device def __device_added_cb(self, device_op): self._check_device(device_op) diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py index 38f1e71..43f0fe9 100644 --- a/src/jarabe/desktop/meshbox.py +++ b/src/jarabe/desktop/meshbox.py @@ -116,7 +116,7 @@ class WirelessNetworkView(CanvasPulsingIcon): self.set_palette(self._palette) self._palette_icon.props.xo_color = self._color - if network.find_connection(self._name) is not None: + if network.find_connection_by_ssid(self._name) is not None: self.props.badge_name = "emblem-favorite" self._palette_icon.props.badge_name = "emblem-favorite" elif initial_ap.flags == network.NM_802_11_AP_FLAGS_PRIVACY: @@ -223,7 +223,7 @@ class WirelessNetworkView(CanvasPulsingIcon): state = network.DEVICE_STATE_UNKNOWN if state == network.DEVICE_STATE_ACTIVATED: - connection = network.find_connection(self._name) + connection = network.find_connection_by_ssid(self._name) if connection: if self._mode == network.NM_802_11_MODE_INFRA: connection.set_connected() @@ -340,11 +340,11 @@ class WirelessNetworkView(CanvasPulsingIcon): self._connect() def _connect(self): - connection = network.find_connection(self._name) + connection = network.find_connection_by_ssid(self._name) if connection is None: settings = Settings() settings.connection.id = 'Auto ' + self._name - settings.connection.uuid = unique_id() + uuid = settings.connection.uuid = unique_id() settings.connection.type = '802-11-wireless' settings.wireless.ssid = self._name @@ -362,7 +362,7 @@ class WirelessNetworkView(CanvasPulsingIcon): if wireless_security is not None: settings.wireless.security = '802-11-wireless-security' - connection = network.add_connection(self._name, settings) + connection = network.add_connection(uuid, settings) obj = self._bus.get_object(_NM_SERVICE, _NM_PATH) netmgr = dbus.Interface(obj, _NM_IFACE) diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py index b3c30d9..696dc91 100644 --- a/src/jarabe/model/network.py +++ b/src/jarabe/model/network.py @@ -1,6 +1,7 @@ # Copyright (C) 2008 Red Hat, Inc. # Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer # Copyright (C) 2009 One Laptop per Child +# Copyright (C) 2009 Paraguay Educa, Martin Abente # # 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 @@ -23,12 +24,15 @@ import time import dbus import gobject import ConfigParser +import gconf from sugar import dispatch from sugar import env +from sugar.util import unique_id DEVICE_TYPE_802_3_ETHERNET = 1 DEVICE_TYPE_802_11_WIRELESS = 2 +DEVICE_TYPE_GSM_MODEM = 3 DEVICE_TYPE_802_11_OLPC_MESH = 6 DEVICE_STATE_UNKNOWN = 0 @@ -42,6 +46,9 @@ DEVICE_STATE_IP_CONFIG = 7 DEVICE_STATE_ACTIVATED = 8 DEVICE_STATE_FAILED = 9 +NM_CONNECTION_TYPE_802_11_WIRELESS = '802-11-wireless' +NM_CONNECTION_TYPE_GSM = 'gsm' + NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0 NM_ACTIVE_CONNECTION_STATE_ACTIVATING = 1 NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2 @@ -81,6 +88,11 @@ NM_CONNECTION_IFACE = 'org.freedesktop.NetworkManagerSettings.Connection' NM_SECRETS_IFACE = 'org.freedesktop.NetworkManagerSettings.Connection.Secrets' NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint' +GSM_USERNAME_PATH = '/sugar/network/gsm/username' +GSM_PASSWORD_PATH = '/sugar/network/gsm/password' +GSM_NUMBER_PATH = '/sugar/network/gsm/number' +GSM_APN_PATH = '/sugar/network/gsm/apn' + _nm_settings = None _conn_counter = 0 @@ -168,6 +180,44 @@ class IP4Config(object): ip4_config['method'] = self.method return ip4_config +class Serial(object): + def __init__(self): + self.baud = None + + def get_dict(self): + serial = {} + + if self.baud is not None: + serial['baud'] = self.baud + + return serial + +class Ppp(object): + def __init__(self): + pass + + def get_dict(self): + ppp = {} + return ppp + +class Gsm(object): + def __init__(self): + self.apn = None + self.number = None + self.username = None + + def get_dict(self): + gsm = {} + + if self.apn is not None: + gsm['apn'] = self.apn + if self.number is not None: + gsm['number'] = self.number + if self.username is not None: + gsm['username'] = self.username + + return gsm + class Settings(object): def __init__(self, wireless_cfg=None): self.connection = Connection() @@ -215,6 +265,35 @@ class Secrets(object): return settings +class SettingsGsm(object): + def __init__(self): + self.connection = Connection() + self.ip4_config = IP4Config() + self.serial = Serial() + self.ppp = Ppp() + self.gsm = Gsm() + + def get_dict(self): + settings = {} + + settings['connection'] = self.connection.get_dict() + settings['serial'] = self.serial.get_dict() + settings['ppp'] = self.ppp.get_dict() + settings['gsm'] = self.gsm.get_dict() + settings['ipv4'] = self.ip4_config.get_dict() + + return settings + +class SecretsGsm(object): + def __init__(self): + self.password = None + + def get_dict(self): + secrets = {} + if self.password is not None: + secrets['password'] = self.password + return {'gsm': secrets} + class NMSettings(dbus.service.Object): def __init__(self): bus = dbus.SystemBus() @@ -233,8 +312,8 @@ class NMSettings(dbus.service.Object): def NewConnection(self, connection_path): pass - def add_connection(self, ssid, conn): - self.connections[ssid] = conn + def add_connection(self, uuid, conn): + self.connections[uuid] = conn conn.secrets_request.connect(self.__secrets_request_cb) self.NewConnection(conn.path) @@ -274,11 +353,13 @@ class NMSettingsConnection(dbus.service.Object): if not self._settings.connection.autoconnect: self._settings.connection.autoconnect = True self._settings.connection.timestamp = int(time.time()) - self.save() + if self._settings.connection.type == NM_CONNECTION_TYPE_802_11_WIRELESS: + self.save() def set_secrets(self, secrets): self._secrets = secrets - self.save() + if self._settings.connection.type == NM_CONNECTION_TYPE_802_11_WIRELESS: + self.save() def get_settings(self): return self._settings @@ -467,7 +548,6 @@ class AccessPoint(gobject.GObject): path=self.model.object_path, dbus_interface=NM_ACCESSPOINT_IFACE) - def get_settings(): global _nm_settings if _nm_settings is None: @@ -478,24 +558,28 @@ def get_settings(): load_connections() return _nm_settings -def find_connection(ssid): +def find_connection_by_ssid(ssid): connections = get_settings().connections - if ssid in connections: - return connections[ssid] - else: - return None -def add_connection(ssid, settings, secrets=None): + for conn_index in connections: + connection = connections[conn_index] + if connection._settings.connection.type == NM_CONNECTION_TYPE_802_11_WIRELESS: + if connection._settings.wireless.ssid == ssid: + return connection + + return None + +def add_connection(uuid, settings, secrets=None): global _conn_counter path = NM_SETTINGS_PATH + '/' + str(_conn_counter) _conn_counter += 1 conn = NMSettingsConnection(path, settings, secrets) - _nm_settings.add_connection(ssid, conn) + _nm_settings.add_connection(uuid, conn) return conn -def load_connections(): +def load_wifi_connections(): profile_path = env.get_profile_path() config_path = os.path.join(profile_path, 'nm', 'connections.cfg') @@ -560,4 +644,43 @@ def load_connections(): except ConfigParser.Error: logging.exception('Error reading section') else: - add_connection(ssid, settings, secrets) + add_connection(uuid, settings, secrets) + + +def load_gsm_connection(): + settings = SettingsGsm() + secrets = SecretsGsm() + + client = gconf.client_get_default() + settings.gsm.username = client.get_string(GSM_USERNAME_PATH) or '' + settings.gsm.number = client.get_string(GSM_NUMBER_PATH) or '' + settings.gsm.apn = client.get_string(GSM_APN_PATH) or '' + password = client.get_string(GSM_PASSWORD_PATH) or '' + + if password: + secrets.password = password + + settings.connection.id = 'gsm' + settings.connection.type = NM_CONNECTION_TYPE_GSM + uuid = settings.connection.uuid = unique_id() + settings.connection.autoconnect = False + settings.ip4_config.method = 'auto' + settings.serial.baud = 115200 + + try: + add_connection(uuid, settings, secrets) + except Exception: + logging.exception('While adding gsm connection') + +def load_connections(): + load_wifi_connections() + load_gsm_connection() + +def find_gsm_connection(): + connections = get_settings().connections + + for connection in connections.values(): + if connection.get_settings().connection.type == NM_CONNECTION_TYPE_GSM: + return connection + + return None -- cgit v0.9.1