Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/jarabe
diff options
context:
space:
mode:
authorMarco Pesenti Gritti <mpgritti@gmail.com>2008-10-22 16:03:04 (GMT)
committer Marco Pesenti Gritti <mpgritti@gmail.com>2008-10-22 16:03:04 (GMT)
commit51eb47d2c0f7b7896e3a8c0ea1e2999d754af8cc (patch)
tree47a31eb2e0fa6e31fdfd431732fc269e06a5cd85 /src/jarabe
parentd5185f7ed4464959beed10ab30a1bc10eb9b0d36 (diff)
Make connection to WEP work. Lots of cleanups left to
in keydialog, really just an hack to make it work there.
Diffstat (limited to 'src/jarabe')
-rw-r--r--src/jarabe/desktop/Makefile.am1
-rw-r--r--src/jarabe/desktop/keydialog.py344
-rw-r--r--src/jarabe/desktop/meshbox.py59
-rw-r--r--src/jarabe/model/network.py24
4 files changed, 410 insertions, 18 deletions
diff --git a/src/jarabe/desktop/Makefile.am b/src/jarabe/desktop/Makefile.am
index 044a943..94d8ab9 100644
--- a/src/jarabe/desktop/Makefile.am
+++ b/src/jarabe/desktop/Makefile.am
@@ -9,6 +9,7 @@ sugar_PYTHON = \
groupbox.py \
homebox.py \
homewindow.py \
+ keydialog.py \
meshbox.py \
myicon.py \
proc_smaps.py \
diff --git a/src/jarabe/desktop/keydialog.py b/src/jarabe/desktop/keydialog.py
new file mode 100644
index 0000000..b992db5
--- /dev/null
+++ b/src/jarabe/desktop/keydialog.py
@@ -0,0 +1,344 @@
+# vi: ts=4 ai noet
+#
+# Copyright (C) 2006-2007 Red Hat, Inc.
+#
+# 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 md5
+from gettext import gettext as _
+
+import gtk
+
+IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001
+IW_AUTH_ALG_SHARED_KEY = 0x00000002
+
+IW_AUTH_WPA_VERSION_DISABLED = 0x00000001
+IW_AUTH_WPA_VERSION_WPA = 0x00000002
+IW_AUTH_WPA_VERSION_WPA2 = 0x00000004
+
+NM_802_11_CAP_NONE = 0x00000000
+NM_802_11_CAP_PROTO_NONE = 0x00000001
+NM_802_11_CAP_PROTO_WEP = 0x00000002
+NM_802_11_CAP_PROTO_WPA = 0x00000004
+NM_802_11_CAP_PROTO_WPA2 = 0x00000008
+NM_802_11_CAP_KEY_MGMT_PSK = 0x00000040
+NM_802_11_CAP_KEY_MGMT_802_1X = 0x00000080
+NM_802_11_CAP_CIPHER_WEP40 = 0x00001000
+NM_802_11_CAP_CIPHER_WEP104 = 0x00002000
+NM_802_11_CAP_CIPHER_TKIP = 0x00004000
+NM_802_11_CAP_CIPHER_CCMP = 0x00008000
+
+NM_AUTH_TYPE_WPA_PSK_AUTO = 0x00000000
+IW_AUTH_CIPHER_NONE = 0x00000001
+IW_AUTH_CIPHER_WEP40 = 0x00000002
+IW_AUTH_CIPHER_TKIP = 0x00000004
+IW_AUTH_CIPHER_CCMP = 0x00000008
+IW_AUTH_CIPHER_WEP104 = 0x00000010
+
+IW_AUTH_KEY_MGMT_802_1X = 0x1
+IW_AUTH_KEY_MGMT_PSK = 0x2
+
+def string_is_hex(key):
+ is_hex = True
+ for c in key:
+ if not 'a' <= c.lower() <= 'f' and not '0' <= c <= '9':
+ is_hex = False
+ return is_hex
+
+def string_is_ascii(string):
+ try:
+ string.encode('ascii')
+ return True
+ except UnicodeEncodeError:
+ return False
+
+def string_to_hex(passphrase):
+ key = ''
+ for c in passphrase:
+ key += '%02x' % ord(c)
+ return key
+
+def hash_passphrase(passphrase):
+ # passphrase must have a length of 64
+ if len(passphrase) > 64:
+ passphrase = passphrase[:64]
+ elif len(passphrase) < 64:
+ while len(passphrase) < 64:
+ passphrase += passphrase[:64 - len(passphrase)]
+ passphrase = md5.new(passphrase).digest()
+ return string_to_hex(passphrase)[:26]
+
+class KeyDialog(gtk.Dialog):
+ def __init__(self, ssid, caps, async_cb, async_err_cb):
+ gtk.Dialog.__init__(self, flags=gtk.DIALOG_MODAL)
+ self.set_title("Wireless Key Required")
+
+ self._async_cb = async_cb
+ self._async_err_cb = async_err_cb
+ self._entry = None
+ self._ssid = ssid
+ self._caps = caps
+
+ self.set_has_separator(False)
+
+ label = gtk.Label("A wireless encryption key is required for\n" \
+ " the wireless network '%s'." % self._ssid)
+ self.vbox.pack_start(label)
+
+ self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OK, gtk.RESPONSE_OK)
+ self.set_default_response(gtk.RESPONSE_OK)
+ self.set_has_separator(True)
+
+ def add_key_entry(self):
+ self._entry = gtk.Entry()
+ #self._entry.props.visibility = False
+ self._entry.connect('changed', self._update_response_sensitivity)
+ self._entry.connect('activate', self._entry_activate_cb)
+ self.vbox.pack_start(self._entry)
+ self.vbox.set_spacing(6)
+ self.vbox.show_all()
+
+ self._update_response_sensitivity()
+ self._entry.grab_focus()
+
+ def _entry_activate_cb(self, entry):
+ self.response(gtk.RESPONSE_OK)
+
+ def create_security(self):
+ raise NotImplementedError
+
+ def get_callbacks(self):
+ return (self._async_cb, self._async_err_cb)
+
+WEP_PASSPHRASE = 1
+WEP_HEX = 2
+WEP_ASCII = 3
+
+class WEPKeyDialog(KeyDialog):
+ def __init__(self, ssid, caps, async_cb, async_err_cb):
+ KeyDialog.__init__(self, ssid, caps, async_cb, async_err_cb)
+
+ # WEP key type
+ self.key_store = gtk.ListStore(str, int)
+ self.key_store.append(["Passphrase (128-bit)", WEP_PASSPHRASE])
+ self.key_store.append(["Hex (40/128-bit)", WEP_HEX])
+ self.key_store.append(["ASCII (40/128-bit)", WEP_ASCII])
+
+ self.key_combo = gtk.ComboBox(self.key_store)
+ cell = gtk.CellRendererText()
+ self.key_combo.pack_start(cell, True)
+ self.key_combo.add_attribute(cell, 'text', 0)
+ self.key_combo.set_active(0)
+ self.key_combo.connect('changed', self._key_combo_changed_cb)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(gtk.Label(_("Key Type:")))
+ hbox.pack_start(self.key_combo)
+ hbox.show_all()
+ self.vbox.pack_start(hbox)
+
+ # Key entry field
+ self.add_key_entry()
+
+ # WEP authentication mode
+ self.auth_store = gtk.ListStore(str, int)
+ self.auth_store.append(["Open System", IW_AUTH_ALG_OPEN_SYSTEM])
+ self.auth_store.append(["Shared Key", IW_AUTH_ALG_SHARED_KEY])
+
+ self.auth_combo = gtk.ComboBox(self.auth_store)
+ cell = gtk.CellRendererText()
+ self.auth_combo.pack_start(cell, True)
+ self.auth_combo.add_attribute(cell, 'text', 0)
+ self.auth_combo.set_active(0)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(gtk.Label(_("Authentication Type:")))
+ hbox.pack_start(self.auth_combo)
+ hbox.show_all()
+
+ self.vbox.pack_start(hbox)
+
+ def _key_combo_changed_cb(self, widget):
+ self._update_response_sensitivity()
+
+ def _get_security(self):
+ key = self._entry.get_text()
+
+ it = self.key_combo.get_active_iter()
+ (key_type, ) = self.key_store.get(it, 1)
+
+ if key_type == WEP_PASSPHRASE:
+ key = hash_passphrase(key)
+ elif key_type == WEP_ASCII:
+ key = string_to_hex(key)
+
+ it = self.auth_combo.get_active_iter()
+ (auth_alg, ) = self.auth_store.get(it, 1)
+
+ we_cipher = None
+ if len(key) == 26:
+ we_cipher = IW_AUTH_CIPHER_WEP104
+ elif len(key) == 10:
+ we_cipher = IW_AUTH_CIPHER_WEP40
+
+ return (we_cipher, key, auth_alg)
+
+ def print_security(self):
+ (we_cipher, key, auth_alg) = self._get_security()
+ print "Cipher: %d" % we_cipher
+ print "Key: %s" % key
+ print "Auth: %d" % auth_alg
+
+ def create_security(self):
+ (we_cipher, key, auth_alg) = self._get_security()
+ return { "802-11-wireless-security": { "wep-key0": key } }
+
+ def _update_response_sensitivity(self, ignored=None):
+ key = self._entry.get_text()
+ it = self.key_combo.get_active_iter()
+ (key_type, ) = self.key_store.get(it, 1)
+
+ valid = False
+ if key_type == WEP_PASSPHRASE:
+ # As the md5 passphrase can be of any length and has no indicator,
+ # we cannot check for the validity of the input.
+ if len(key) > 0:
+ valid = True
+ elif key_type == WEP_ASCII:
+ if len(key) == 5 or len(key) == 13:
+ valid = string_is_ascii(key)
+ elif key_type == WEP_HEX:
+ if len(key) == 10 or len(key) == 26:
+ valid = string_is_hex(key)
+
+ self.set_response_sensitive(gtk.RESPONSE_OK, valid)
+
+class WPAKeyDialog(KeyDialog):
+ def __init__(self, ssid, caps, async_cb, async_err_cb):
+ KeyDialog.__init__(self, ssid, caps, async_cb, async_err_cb)
+ self.add_key_entry()
+
+ self.store = gtk.ListStore(str, int)
+ self.store.append(["Automatic", NM_AUTH_TYPE_WPA_PSK_AUTO])
+ if caps & NM_802_11_CAP_CIPHER_CCMP:
+ self.store.append(["AES-CCMP", IW_AUTH_CIPHER_CCMP])
+ if caps & NM_802_11_CAP_CIPHER_TKIP:
+ self.store.append(["TKIP", IW_AUTH_CIPHER_TKIP])
+
+ self.combo = gtk.ComboBox(self.store)
+ cell = gtk.CellRendererText()
+ self.combo.pack_start(cell, True)
+ self.combo.add_attribute(cell, 'text', 0)
+ self.combo.set_active(0)
+
+ self.hbox = gtk.HBox()
+ self.hbox.pack_start(gtk.Label(_("Encryption Type:")))
+ self.hbox.pack_start(self.combo)
+ self.hbox.show_all()
+
+ self.vbox.pack_start(self.hbox)
+
+ def _get_security(self):
+ ssid = self._ssid
+ key = self._entry.get_text()
+ is_hex = string_is_hex(key)
+
+ real_key = None
+ if len(key) == 64 and is_hex:
+ # Hex key
+ real_key = key
+ elif len(key) >= 8 and len(key) <= 63:
+ # passphrase
+ from subprocess import Popen, PIPE
+ p = Popen(['/usr/sbin/wpa_passphrase', ssid, key], stdout=PIPE)
+ for line in p.stdout:
+ if line.strip().startswith("psk="):
+ real_key = line.strip()[4:]
+ if p.wait() != 0:
+ raise RuntimeError("Error hashing passphrase")
+ if real_key and len(real_key) != 64:
+ real_key = None
+
+ if not real_key:
+ raise RuntimeError("Invalid key")
+
+ it = self.combo.get_active_iter()
+ (we_cipher, ) = self.store.get(it, 1)
+
+ wpa_ver = IW_AUTH_WPA_VERSION_WPA
+ if self._caps & NM_802_11_CAP_PROTO_WPA2:
+ wpa_ver = IW_AUTH_WPA_VERSION_WPA2
+
+ return (we_cipher, real_key, wpa_ver)
+
+ def print_security(self):
+ (we_cipher, key, wpa_ver) = self._get_security()
+ print "Cipher: %d" % we_cipher
+ print "Key: %s" % key
+ print "WPA Ver: %d" % wpa_ver
+
+ def create_security(self):
+ (we_cipher, key, wpa_ver) = self._get_security()
+ from nminfo import Security
+ return Security.new_from_args(we_cipher,
+ (key, wpa_ver, IW_AUTH_KEY_MGMT_PSK))
+
+ def _update_response_sensitivity(self, ignored=None):
+ key = self._entry.get_text()
+ is_hex = string_is_hex(key)
+
+ valid = False
+ if len(key) == 64 and is_hex:
+ # hex key
+ valid = True
+ elif len(key) >= 8 and len(key) <= 63:
+ # passphrase
+ valid = True
+ self.set_response_sensitive(gtk.RESPONSE_OK, valid)
+ return False
+
+def create(ssid, caps, async_cb, async_err_cb):
+ if (caps & NM_802_11_CAP_CIPHER_TKIP or caps & NM_802_11_CAP_CIPHER_CCMP) \
+ and (caps & NM_802_11_CAP_PROTO_WPA or \
+ caps & NM_802_11_CAP_PROTO_WPA2):
+ key_dialog = WPAKeyDialog(ssid, caps, async_cb, async_err_cb)
+ else:
+ key_dialog = WEPKeyDialog(ssid, caps, async_cb, async_err_cb)
+
+ key_dialog.connect("response", _key_dialog_response_cb)
+ key_dialog.connect("destroy", _key_dialog_destroy_cb)
+ key_dialog.show_all()
+
+def _key_dialog_destroy_cb(key_dialog, data=None):
+ _key_dialog_response_cb(key_dialog, gtk.RESPONSE_CANCEL)
+
+def _key_dialog_response_cb(key_dialog, response_id):
+ (async_cb, async_err_cb) = key_dialog.get_callbacks()
+ security = None
+ if response_id == gtk.RESPONSE_OK:
+ security = key_dialog.create_security()
+ key_dialog.destroy()
+
+ if response_id in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_NONE]:
+ # key dialog dialog was canceled; send the error back to NM
+ async_err_cb(CanceledKeyRequestError())
+ elif response_id == gtk.RESPONSE_OK:
+ if not security:
+ raise RuntimeError("Invalid security arguments.")
+ async_cb(security)
+ else:
+ raise RuntimeError("Unhandled key dialog response %d" % response_id)
+
diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
index 83474e8..ae17091 100644
--- a/src/jarabe/desktop/meshbox.py
+++ b/src/jarabe/desktop/meshbox.py
@@ -40,6 +40,7 @@ from jarabe.view.buddyicon import BuddyIcon
from jarabe.view.pulsingicon import CanvasPulsingIcon
from jarabe.desktop.snowflakelayout import SnowflakeLayout
from jarabe.desktop.spreadlayout import SpreadLayout
+from jarabe.desktop import keydialog
from jarabe.model import bundleregistry
from jarabe.model import network
@@ -49,6 +50,7 @@ _NM_PATH = '/org/freedesktop/NetworkManager'
_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
_NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless'
_NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
+_NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
_ICON_NAME = 'network-wireless'
@@ -65,6 +67,7 @@ class AccessPointView(CanvasPulsingIcon):
self._name = ''
self._strength = 0
self._flags = 0
+ self._wpa_flags = 0
self._device_state = None
self._active = True
@@ -138,12 +141,15 @@ class AccessPointView(CanvasPulsingIcon):
self._update_state()
def _update_properties(self, props):
+ logging.debug(props)
if 'Ssid' in props:
self._name = props['Ssid']
if 'Strength' in props:
self._strength = props['Strength']
if 'Flags' in props:
self._flags = props['Flags']
+ if 'WpaFlags' in props:
+ self._wpa_flags = props['WpaFlags']
self._update()
@@ -239,10 +245,14 @@ class AccessPointView(CanvasPulsingIcon):
conn = network.find_connection(self._name)
if conn is None:
info = { 'connection': { 'id' : 'Auto ' + self._name,
- 'uuid' : unique_id(),
- 'type' : '802-11-wireless' } ,
- '802-11-wireless' : { 'ssid': self._name }
+ 'uuid' : unique_id(),
+ 'type' : '802-11-wireless' } ,
+ '802-11-wireless' : { 'ssid': self._name }
}
+
+ if self._flags == network.AP_FLAGS_802_11_PRIVACY:
+ info["802-11-wireless-security"] = { "key-mgmt": "none" }
+
conn = network.add_connection(self._name, info)
obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
@@ -263,6 +273,9 @@ class AccessPointView(CanvasPulsingIcon):
self._greyed_out = self._name.lower().find(query) == -1
self._update_state()
+ def create_keydialog(self, reply, error):
+ keydialog.create(self._name, self._wpa_flags, reply, error)
+
def disconnect(self):
self._bus.remove_signal_receiver(self.__ap_properties_changed_cb,
signal_name='PropertiesChanged',
@@ -502,17 +515,18 @@ class NetworkManagerObserver(object):
self._box = box
self._bus = dbus.SystemBus()
self._devices = {}
+ self._netmgr = None
def listen(self):
try:
obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
- netmgr = dbus.Interface(obj, _NM_IFACE)
+ self._netmgr = dbus.Interface(obj, _NM_IFACE)
except dbus.DBusException:
logging.debug('%s service not available', _NM_SERVICE)
return
- netmgr.GetDevices(reply_handler=self._get_devices_reply_cb,
- error_handler=self._get_devices_error_cb)
+ self._netmgr.GetDevices(reply_handler=self.__get_devices_reply_cb,
+ error_handler=self.__get_devices_error_cb)
self._bus.add_signal_receiver(self.__device_added_cb,
signal_name='DeviceAdded',
@@ -521,11 +535,27 @@ class NetworkManagerObserver(object):
signal_name='DeviceRemoved',
dbus_interface=_NM_DEVICE_IFACE)
- def _get_devices_reply_cb(self, devices_o):
+ settings = network.get_settings()
+ settings.secrets_request.connect(self.__secrets_request_cb)
+
+ def __secrets_request_cb(self, **kwargs):
+ netmgr_props = dbus.Interface(
+ self._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')
+ ap_o = props.Get(_NM_ACTIVE_CONN_IFACE, 'SpecificObject')
+
+ ap_view = self._box.access_points[ap_o]
+ ap_view.create_keydialog(kwargs['reply'], kwargs['error'])
+
+ def __get_devices_reply_cb(self, devices_o):
for dev_o in devices_o:
self._check_device(dev_o)
- def _get_devices_error_cb(self, err):
+ def __get_devices_error_cb(self, err):
logging.error('Failed to get devices: %s', err)
def _check_device(self, device_o):
@@ -556,10 +586,11 @@ class MeshBox(gtk.VBox):
gobject.GObject.__init__(self)
+ self.access_points = {}
+
self._model = neighborhood.get_model()
self._buddies = {}
self._activities = {}
- self._access_points = {}
self._mesh = {}
self._buddy_to_activity = {}
self._suspended = True
@@ -696,24 +727,24 @@ class MeshBox(gtk.VBox):
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
- self._access_points[ap.object_path] = icon
+ self.access_points[ap.object_path] = icon
def remove_access_point(self, ap_o):
- icon = self._access_points[ap_o]
+ icon = self.access_points[ap_o]
icon.disconnect()
self._layout.remove(icon)
- del self._access_points[ap_o]
+ del self.access_points[ap_o]
def suspend(self):
if not self._suspended:
self._suspended = True
- for ap in self._access_points.values():
+ for ap in self.access_points.values():
ap.props.paused = True
def resume(self):
if self._suspended:
self._suspended = False
- for ap in self._access_points.values():
+ for ap in self.access_points.values():
ap.props.paused = False
def _toolbar_query_changed_cb(self, toolbar, query):
diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py
index 21047c5..a7a6bd1 100644
--- a/src/jarabe/model/network.py
+++ b/src/jarabe/model/network.py
@@ -14,6 +14,8 @@
# 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
+
import dbus
from sugar import dispatch
@@ -49,7 +51,9 @@ class NMSettings(dbus.service.Object):
bus = dbus.SystemBus()
bus_name = dbus.service.BusName(SETTINGS_SERVICE, bus=bus)
dbus.service.Object.__init__(self, bus_name, NM_SETTINGS_PATH)
+
self.connections = {}
+ self.secrets_request = dispatch.Signal()
@dbus.service.method(dbus_interface=NM_SETTINGS_IFACE,
in_signature='', out_signature='ao')
@@ -62,8 +66,14 @@ class NMSettings(dbus.service.Object):
def add_connection(self, ssid, conn):
self.connections[ssid] = conn
+ conn.secrets_request.connect(self.__secrets_request_cb)
self.NewConnection(conn.path)
+ def __secrets_request_cb(self, sender, **kwargs):
+ self.secrets_request.send(self, connection=sender,
+ reply=kwargs['reply'],
+ error=kwargs['error'])
+
class NMSettingsConnection(dbus.service.Object):
def __init__(self, path, settings, secrets):
bus = dbus.SystemBus()
@@ -82,12 +92,18 @@ class NMSettingsConnection(dbus.service.Object):
return self._settings
@dbus.service.method(dbus_interface=NM_SECRETS_IFACE,
+ async_callbacks=('reply', 'error'),
in_signature='sasb', out_signature='a{sa{sv}}')
- def GetSecrets(self, setting_name, hints, request_new):
- if request_new or self._secrets is None:
- self.secrets_request.send(self)
+ def GetSecrets(self, setting_name, hints, request_new, reply, error):
+ logging.debug('Secrets requested for connection %s', self.path)
- return self._secrets
+ if request_new or self._secrets is None:
+ try:
+ self.secrets_request.send(self, reply=reply, error=error)
+ except Exception, e:
+ logging.error(e)
+ else:
+ reply(self._secrets)
def get_settings():
global _nm_settings