[Sugar-devel] [PATCH sugar-0.94] Don't treat SSID as UTF-8 character sequence (fixes SL#2023)
Sascha Silbe
silbe at activitycentral.com
Tue Apr 10 14:39:18 EDT 2012
This is a backport of 7f8ba95a66780828531eba0494e004757bf45c71.
IEEE 802.11 [2] defines the SSID as a sequence of octets (i.e. bytes), but
Sugar treated it as UTF-8 character data. While in most cases the SSID is
actually some human-readable string, there's neither a guarantee for that nor
does any (de-facto or de-jure) standard specify the encoding to use. As a
result, we'll encounter SSIDs in a large variety of encodings and will also
need to cope with arbitrary byte strings. Any assumption of a single (or in
fact any) character encoding is incorrect.
The D-Bus API of NetworkManager 0.9 [3] passes SSIDs as uninterpreted byte
strings (D-Bus signature "ay"). Before SSIDs can be displayed on screen, some
kind of interpretation must happen.
NetworkManager has a rather elaborate heuristic that takes the user locale into
account. In the future (i.e. when the NetworkManager client code in Sugar has
been ported to gobject-introspection) we may use nm_utils_ssid_to_utf8() [4],
but for now we're doing the following to allow the user to use non-UTF-8 APs at
all:
1. If the SSID is a valid character string consisting only of
printable characters in one of the following encodings (tried in
the given order), decode it accordingly:
UTF-8, ISO-8859-1, Windows-1251.
2. Return a hex dump of the SSID.
The first rule should cover the majority of current Sugar users and hopefully
all AP vendors will switch to UTF-8 eventually. In the meantime, the second
rule allows users to distinguish between several APs with SSIDs in unknown
encodings (or even using arbitrary byte sequences that don't _have_ a character
representation).
This backport was not tested in any way.
[1] https://bugs.sugarlabs.org/ticket/2023
[2] http://standards.ieee.org/getieee802/download/802.11-2007.pdf
[3] http://projects.gnome.org/NetworkManager/developers/api/09/spec.html
[4] http://projects.gnome.org/NetworkManager/developers/libnm-util/09/libnm-util-nm-utils.html#nm-utils-ssid-to-utf8
Signed-off-by: Sascha Silbe <silbe at activitycentral.com>
---
extensions/deviceicon/network.py | 17 +++++++------
src/jarabe/desktop/keydialog.py | 5 ++--
src/jarabe/desktop/meshbox.py | 4 +--
src/jarabe/desktop/networkviews.py | 32 +++++++++++------------
src/jarabe/model/adhoc.py | 6 ++---
src/jarabe/model/network.py | 49 +++++++++++++++++++++++++++++++++---
6 files changed, 80 insertions(+), 33 deletions(-)
diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
index 789ea13..713a741 100644
--- a/extensions/deviceicon/network.py
+++ b/extensions/deviceicon/network.py
@@ -385,7 +385,8 @@ class WirelessDeviceView(ToolButton):
self._device = device
self._device_props = None
self._flags = 0
- self._name = ''
+ self._ssid = ''
+ self._display_name = ''
self._mode = network.NM_802_11_MODE_UNKNOWN
self._strength = 0
self._frequency = 0
@@ -405,7 +406,7 @@ class WirelessDeviceView(ToolButton):
self._icon.show()
self.set_palette_invoker(FrameWidgetInvoker(self))
- self._palette = WirelessPalette(self._name)
+ self._palette = WirelessPalette(self._display_name)
self._palette.connect('deactivate-connection',
self.__deactivate_connection_cb)
self.set_palette(self._palette)
@@ -482,7 +483,8 @@ class WirelessDeviceView(ToolButton):
self._mode = properties['Mode']
self._color = None
if 'Ssid' in properties:
- self._name = properties['Ssid']
+ self._ssid = properties['Ssid']
+ self._display_name = network.ssid_to_display_name(self._ssid)
self._color = None
if 'Strength' in properties:
self._strength = properties['Strength']
@@ -493,11 +495,11 @@ class WirelessDeviceView(ToolButton):
if self._color == None:
if self._mode == network.NM_802_11_MODE_ADHOC and \
- network.is_sugar_adhoc_network(self._name):
+ network.is_sugar_adhoc_network(self._ssid):
self._color = profile.get_color()
else:
sha_hash = hashlib.sha1()
- data = self._name + hex(self._flags)
+ data = self._ssid + hex(self._flags)
sha_hash.update(data)
digest = hash(sha_hash.digest())
index = digest % len(xocolor.colors)
@@ -519,7 +521,8 @@ class WirelessDeviceView(ToolButton):
else:
self._icon.props.badge_name = None
- self._palette.props.primary_text = glib.markup_escape_text(self._name)
+ label = glib.markup_escape_text(self._display_name)
+ self._palette.props.primary_text = label
self._update_state()
self._update_color()
@@ -531,7 +534,7 @@ class WirelessDeviceView(ToolButton):
state = network.DEVICE_STATE_UNKNOWN
if self._mode != network.NM_802_11_MODE_ADHOC and \
- network.is_sugar_adhoc_network(self._name) == False:
+ network.is_sugar_adhoc_network(self._ssid) == False:
if state == network.DEVICE_STATE_ACTIVATED:
icon_name = '%s-connected' % 'network-wireless'
else:
diff --git a/src/jarabe/desktop/keydialog.py b/src/jarabe/desktop/keydialog.py
index c72f498..cfe56f7 100644
--- a/src/jarabe/desktop/keydialog.py
+++ b/src/jarabe/desktop/keydialog.py
@@ -90,8 +90,9 @@ class KeyDialog(gtk.Dialog):
self.set_has_separator(False)
- label = gtk.Label("A wireless encryption key is required for\n" \
- " the wireless network '%s'." % self._ssid)
+ display_name = network.ssid_to_display_name(ssid)
+ label = gtk.Label(_("A wireless encryption key is required for\n"
+ " the wireless network '%s'.") % (display_name, ))
self.vbox.pack_start(label)
self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
index 6d5bb48..c111e7e 100644
--- a/src/jarabe/desktop/meshbox.py
+++ b/src/jarabe/desktop/meshbox.py
@@ -554,13 +554,13 @@ class MeshBox(gtk.VBox):
# if we have mesh hardware, ignore OLPC mesh networks that appear as
# normal wifi networks
if len(self._mesh) > 0 and ap.mode == network.NM_802_11_MODE_ADHOC \
- and ap.name == 'olpc-mesh':
+ and ap.ssid == 'olpc-mesh':
logging.debug('ignoring OLPC mesh IBSS')
ap.disconnect()
return
if self._adhoc_manager is not None and \
- network.is_sugar_adhoc_network(ap.name) and \
+ network.is_sugar_adhoc_network(ap.ssid) and \
ap.mode == network.NM_802_11_MODE_ADHOC:
if old_hash_value is None:
# new Ad-hoc network finished initializing
diff --git a/src/jarabe/desktop/networkviews.py b/src/jarabe/desktop/networkviews.py
index 677452d..66e34b8 100644
--- a/src/jarabe/desktop/networkviews.py
+++ b/src/jarabe/desktop/networkviews.py
@@ -69,7 +69,8 @@ class WirelessNetworkView(CanvasPulsingIcon):
self._disconnect_item = None
self._connect_item = None
self._filtered = False
- self._name = initial_ap.name
+ self._ssid = initial_ap.ssid
+ self._display_name = network.ssid_to_display_name(self._ssid)
self._mode = initial_ap.mode
self._strength = initial_ap.strength
self._flags = initial_ap.flags
@@ -80,11 +81,11 @@ class WirelessNetworkView(CanvasPulsingIcon):
self._color = None
if self._mode == network.NM_802_11_MODE_ADHOC and \
- network.is_sugar_adhoc_network(self._name):
+ network.is_sugar_adhoc_network(self._ssid):
self._color = profile.get_color()
else:
sha_hash = hashlib.sha1()
- data = self._name + hex(self._flags)
+ data = self._ssid + hex(self._flags)
sha_hash.update(data)
digest = hash(sha_hash.digest())
index = digest % len(xocolor.colors)
@@ -130,8 +131,8 @@ class WirelessNetworkView(CanvasPulsingIcon):
icon_size=style.STANDARD_ICON_SIZE,
badge_name=self.props.badge_name)
- p = palette.Palette(primary_text=glib.markup_escape_text(self._name),
- icon=self._palette_icon)
+ label = glib.markup_escape_text(self._display_name)
+ p = palette.Palette(primary_text=label, icon=self._palette_icon)
self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
self._connect_item.connect('activate', self.__connect_activate_cb)
@@ -189,7 +190,7 @@ class WirelessNetworkView(CanvasPulsingIcon):
def _update_icon(self):
if self._mode == network.NM_802_11_MODE_ADHOC and \
- network.is_sugar_adhoc_network(self._name):
+ network.is_sugar_adhoc_network(self._ssid):
channel = max([1] + [ap.channel for ap in
self._access_points.values()])
if self._device_state == network.DEVICE_STATE_ACTIVATED and \
@@ -215,7 +216,7 @@ class WirelessNetworkView(CanvasPulsingIcon):
def _update_badge(self):
if self._mode != network.NM_802_11_MODE_ADHOC:
- if network.find_connection_by_ssid(self._name) is not None:
+ if network.find_connection_by_ssid(self._ssid) is not None:
self.props.badge_name = 'emblem-favorite'
self._palette_icon.props.badge_name = 'emblem-favorite'
elif self._flags == network.NM_802_11_AP_FLAGS_PRIVACY:
@@ -244,7 +245,7 @@ class WirelessNetworkView(CanvasPulsingIcon):
self._palette.props.secondary_text = _('Connecting...')
self.props.pulsing = True
elif state == network.DEVICE_STATE_ACTIVATED:
- connection = network.find_connection_by_ssid(self._name)
+ connection = network.find_connection_by_ssid(self._ssid)
if connection is not None:
if self._mode == network.NM_802_11_MODE_INFRA:
connection.set_connected()
@@ -270,7 +271,7 @@ class WirelessNetworkView(CanvasPulsingIcon):
def _disconnect_activate_cb(self, item):
if self._mode == network.NM_802_11_MODE_INFRA:
- connection = network.find_connection_by_ssid(self._name)
+ connection = network.find_connection_by_ssid(self._ssid)
if connection:
connection.disable_autoconnect()
@@ -347,13 +348,13 @@ class WirelessNetworkView(CanvasPulsingIcon):
self._connect()
def _connect(self):
- connection = network.find_connection_by_ssid(self._name)
+ connection = network.find_connection_by_ssid(self._ssid)
if connection is None:
settings = Settings()
- settings.connection.id = 'Auto ' + self._name
+ settings.connection.id = 'Auto ' + self._display_name
uuid = settings.connection.uuid = unique_id()
settings.connection.type = '802-11-wireless'
- settings.wireless.ssid = self._name
+ settings.wireless.ssid = self._ssid
if self._mode == network.NM_802_11_MODE_INFRA:
settings.wireless.mode = 'infrastructure'
@@ -387,12 +388,12 @@ class WirelessNetworkView(CanvasPulsingIcon):
logging.error('Failed to activate connection: %s', err)
def set_filter(self, query):
- self._filtered = self._name.lower().find(query) == -1
+ self._filtered = self._display_name.lower().find(query) == -1
self._update_icon()
self._update_color()
def create_keydialog(self, settings, response):
- keydialog.create(self._name, self._flags, self._wpa_flags,
+ keydialog.create(self._ssid, self._flags, self._wpa_flags,
self._rsn_flags, self._device_caps, settings,
response)
@@ -433,7 +434,7 @@ class WirelessNetworkView(CanvasPulsingIcon):
def is_olpc_mesh(self):
return self._mode == network.NM_802_11_MODE_ADHOC \
- and self.name == 'olpc-mesh'
+ and self._ssid == 'olpc-mesh'
def remove_all_aps(self):
for ap in self._access_points.values():
@@ -601,7 +602,6 @@ class OlpcMeshView(CanvasPulsingIcon):
self._disconnect_item = None
self._connect_item = None
self._filtered = False
- self._name = ''
self._device_state = None
self._active = False
device = mesh_mgr.mesh_device
diff --git a/src/jarabe/model/adhoc.py b/src/jarabe/model/adhoc.py
index 647bd8e..ab540fa 100644
--- a/src/jarabe/model/adhoc.py
+++ b/src/jarabe/model/adhoc.py
@@ -249,13 +249,13 @@ class AdHocManager(gobject.GObject):
access_point -- Access Point
"""
- if access_point.name.endswith(' 1'):
+ if access_point.ssid.endswith(' 1'):
self._networks[self._CHANNEL_1] = access_point
self.emit('members-changed', self._CHANNEL_1, True)
- elif access_point.name.endswith(' 6'):
+ elif access_point.ssid.endswith(' 6'):
self._networks[self._CHANNEL_6] = access_point
self.emit('members-changed', self._CHANNEL_6, True)
- elif access_point.name.endswith('11'):
+ elif access_point.ssid.endswith('11'):
self._networks[self._CHANNEL_11] = access_point
self.emit('members-changed', self._CHANNEL_11, True)
diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py
index f265ae4..63e1ff0 100644
--- a/src/jarabe/model/network.py
+++ b/src/jarabe/model/network.py
@@ -714,7 +714,7 @@ class AccessPoint(gobject.GObject):
self._initialized = False
self._bus = dbus.SystemBus()
- self.name = ''
+ self.ssid = ''
self.strength = 0
self.flags = 0
self.wpa_flags = 0
@@ -768,7 +768,7 @@ class AccessPoint(gobject.GObject):
else:
fl |= 1 << 6
- hashstr = str(fl) + '@' + self.name
+ hashstr = str(fl) + '@' + self.ssid
return hash(hashstr)
def _update_properties(self, properties):
@@ -778,7 +778,7 @@ class AccessPoint(gobject.GObject):
old_hash = None
if 'Ssid' in properties:
- self.name = properties['Ssid']
+ self.ssid = properties['Ssid']
if 'Strength' in properties:
self.strength = properties['Strength']
if 'Flags' in properties:
@@ -1005,3 +1005,46 @@ def disconnect_access_points(ap_paths):
dev_obj = bus.get_object(NM_SERVICE, dev_path)
dev = dbus.Interface(dev_obj, NM_DEVICE_IFACE)
dev.Disconnect()
+
+
+def _is_non_printable(char):
+ """
+ Return True if char is a non-printable unicode character, False otherwise
+ """
+ return (char < u' ') or (u'~' < char < u'\xA0') or (char == u'\xAD')
+
+
+def ssid_to_display_name(ssid):
+ """Convert an SSID into a unicode string for recognising Access Points
+
+ Return a unicode string that's useful for recognising and
+ distinguishing between Access Points (APs).
+
+ IEEE 802.11 defines SSIDs as arbitrary byte sequences. As random
+ bytes are not very user-friendly, most APs use some human-readable
+ character string as SSID. However, because there's no standard
+ specifying what encoding to use, AP vendors chose various
+ different encodings. Since there's also no indication of what
+ encoding was used for a particular SSID, the best we can do for
+ turning an SSID into a displayable string is to try a couple of
+ encodings based on some heuristic.
+
+ We're currently using the following heuristic:
+
+ 1. If the SSID is a valid character string consisting only of
+ printable characters in one of the following encodings (tried in
+ the given order), decode it accordingly:
+ UTF-8, ISO-8859-1, Windows-1251.
+ 2. Return a hex dump of the SSID.
+ """
+ for encoding in ['utf-8', 'iso-8859-1', 'windows-1251']:
+ try:
+ display_name = unicode(ssid, encoding)
+ except UnicodeDecodeError:
+ continue
+
+ if not [True for char in display_name if _is_non_printable(char)]:
+ # Only printable characters
+ return display_name
+
+ return ':'.join(['%02x' % (ord(byte), ) for byte in ssid])
--
1.7.9.5
More information about the Sugar-devel
mailing list