diff --git a/services/nm/nmclient.py b/services/nm/nmclient.py index 866097da..d79919fe 100644 --- a/services/nm/nmclient.py +++ b/services/nm/nmclient.py @@ -34,6 +34,9 @@ from bubble import Bubble import nminfo +IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001 +IW_AUTH_ALG_SHARED_KEY = 0x00000002 + NM_DEVICE_STAGE_STRINGS=("Unknown", "Prepare", @@ -428,11 +431,20 @@ class NMClientApp: self.nminfo = None self._nm_present = False self._nm_state = NM_STATE_UNKNOWN - self._icon_theme = gtk.icon_theme_get_default() self._update_timer = 0 self._active_device = None self._devices = {} + self._icon_theme = gtk.icon_theme_get_default() + self._icons = {} + self._cur_icon = None + try: + self._icons = self._load_icons() + except RuntimeError: + logging.debug("Couldn't find required icon resources, will exit.") + os._exit(1) + self._setup_trayicon() + self._menu = None self._hover_menu = False self._timeline = Timeline(self) @@ -440,11 +452,8 @@ class NMClientApp: self._timeline.add_tag('before_popdown', 7, 7) self._timeline.add_tag('popdown', 8, 8) - self._icons = {} - self._cur_icon = None - try: - self.nminfo = nminfo.NMInfo() + self.nminfo = nminfo.NMInfo(self) except RuntimeError: pass self._setup_dbus() @@ -452,13 +461,6 @@ class NMClientApp: self._get_nm_state() self._get_initial_devices() - try: - self._icons = self._load_icons() - except RuntimeError: - logging.debug("Couldn't find required icon resources, will exit.") - os._exit(1) - self._setup_trayicon() - def _get_one_icon_pixbuf(self, name): info = self._icon_theme.lookup_icon(name, 75, 0) if not info or not info.get_filename(): @@ -478,9 +480,7 @@ class NMClientApp: def _get_nm_state(self): # Grab NM's state - nm_obj = sys_bus.get_object(NM_SERVICE, NM_PATH) - nm = dbus.Interface(nm_obj, NM_IFACE) - nm.state(reply_handler=self._get_state_reply_cb, \ + self._nm_obj.state(reply_handler=self._get_state_reply_cb, \ error_handler=self._get_state_error_cb) def _get_state_reply_cb(self, state): @@ -654,9 +654,7 @@ class NMClientApp: logging.debug("Error updating devices (%s)" % err) def _get_initial_devices(self): - nm_obj = sys_bus.get_object(NM_SERVICE, NM_PATH) - nm = dbus.Interface(nm_obj, NM_IFACE) - nm.getDevices(reply_handler=self._get_initial_devices_reply_cb, \ + self._nm_obj.getDevices(reply_handler=self._get_initial_devices_reply_cb, \ error_handler=self._get_initial_devices_error_cb) def _add_device(self, dev_op): @@ -697,6 +695,11 @@ class NMClientApp: return self._schedule_icon_update() + def get_device(self, dev_op): + if not self._devices.has_key(dev_op): + return None + return self._devices[dev_op] + def _setup_dbus(self): self._sig_handlers = { 'StateChange': self.state_change_sig_handler, @@ -714,7 +717,8 @@ class NMClientApp: 'WirelessNetworkStrengthChanged': self.wireless_network_strength_changed_sig_handler } - self.nm_proxy = sys_bus.get_object(NM_SERVICE, NM_PATH) + self._nm_proxy = sys_bus.get_object(NM_SERVICE, NM_PATH) + self._nm_obj = dbus.Interface(self._nm_proxy, NM_IFACE) sys_bus.add_signal_receiver(self.name_owner_changed_sig_handler, signal_name="NameOwnerChanged", @@ -757,7 +761,30 @@ class NMClientApp: net_op = "" if network: net_op = network.get_op() - logging.debug("clicked dev %s, net %s" % (device.get_op(), net_op)) + try: + # NM 0.6.4 and earlier have a bug which returns an + # InvalidArguments error if no security information is passed + # for wireless networks + self._nm_obj.setActiveDevice(device.get_op(), network.get_ssid()) + except dbus.DBusException, e: + if str(e).find("invalid arguments"): + pass + else: + raise dbus.DBusException(e) + + self._popdown() + + def get_key_for_network(self, wep_auth_alg=IW_AUTH_ALG_OPEN_SYSTEM): + # Throw up a dialog asking for the key here, and set + # the authentication algorithm to the given one, if any + # + # Key needs to be limited to _either_ 10 or 26 digits long, + # and contain _only_ _hex_ digits, 0-9 or a-f + # + # Auth algorithm should be a dropdown of: [Open System, Shared Key], + # mapping to the values [IW_AUTH_ALG_OPEN_SYSTEM, IW_AUTH_ALG_SHARED_KEY] + # above + pass def device_activation_stage_sig_handler(self, device, stage): print 'Network Manager Device Stage "%s" for device %s'%(NM_DEVICE_STAGE_STRINGS[stage], device) diff --git a/services/nm/nminfo.py b/services/nm/nminfo.py index 746ed7f7..3ba24906 100644 --- a/services/nm/nminfo.py +++ b/services/nm/nminfo.py @@ -36,6 +36,11 @@ class NoNetworks(dbus.DBusException): dbus.DBusException.__init__(self) self._dbus_error_name = NM_INFO_IFACE + '.NoNetworks' +class CanceledKeyRequestError(dbus.DBusException): + def __init__(self): + dbus.DBusException.__init__(self) + self._dbus_error_name = NM_INFO_IFACE + '.CanceledError' + class NetworkInvalidError(Exception): pass @@ -192,6 +197,12 @@ class Network: args += self._security.get_properties() return tuple(args) + def get_security(self): + return self._security.get_properties() + + def set_security(self, security): + self._security = security + def read_from_args(self, auto, fallback, bssid, we_cipher, *args): if auto == False: self.timestamp = time.now() @@ -231,6 +242,11 @@ class Network: self._security.write_to_config(self.ssid, config) +class NotFoundError(dbus.DBusException): + pass +class UnsupportedError(dbus.DBusException): + pass + class NMInfoDBusServiceHelper(dbus.service.Object): def __init__(self, parent): self._parent = parent @@ -270,15 +286,24 @@ class NMInfoDBusServiceHelper(dbus.service.Object): def updateNetworkInfo(self, ssid, bauto, bfallback, bssid, cipher, *args): self._parent.update_network_info(ssid, bauto, bfallback, bssid, cipher, args) + @dbus.service.method(NM_INFO_IFACE, async_callbacks=('async_cb', 'async_err_cb')) + def getKeyForNetwork(self, dev_path, net_path, ssid, attempt, new_key, async_cb, async_err_cb): + self._parent.get_key_for_network(dev_path, net_path, ssid, + attempt, new_key, async_cb, async_err_cb) + + @dbus.service.method(NM_INFO_IFACE) + def cancelGetKeyForNetwork(self): + self._parent.cancel_get_key_for_network() class NMInfo(object): - def __init__(self): + def __init__(self, client): try: profile_path = env.get_profile_path() except NameError: home = os.path.expanduser("~") profile_path = os.path.join(home, ".sugar", "default") self._cfg_file = os.path.join(profile_path, "nm", "networks.cfg") + self._nmclient = client self._allowed_networks = self._read_config() self._dbus_helper = NMInfoDBusServiceHelper(self) @@ -344,3 +369,52 @@ class NMInfo(object): except InvalidNetworkError, e: print "Bad network!! %s" % e del net + + def get_key_for_network(self, dev_op, net_op, ssid, attempt, new_key, async_cb, async_err_cb): + if self._networks.has_key(ssid) and not new_key: + # We've got the info already + net = self._networks[ssid] + async_cb(tuple(net.get_security())) + return + + # Otherwise, ask the user for it + net = None + dev = self._nm_client.get_dev(dev_op) + if not dev: + async_err_cb(NotFoundError("Device was unknown.")) + if dev.get_type() == DEVICE_TYPE_802_3_ETHERNET: + # We don't support wired 802.1x yet... + async_err_cb(UnsupportedError("Device type is unsupported by NMI.")) + + net = dev.get_network(net_op) + if not net: + async_err_cb(NotFoundError("Network was unknown.")) + + try: + (key, auth_alg) = self._nm_client.get_key_for_network() + except CanceledKeyRequestError, e: + # user canceled the key dialog; send the error back to NM + async_err_cb(e) + return + + if not key or not auth_alg: + # no key returned, *** BUG ***; the key dialog + # should always return either a key + auth_alg, or a + #cancel error + raise RuntimeError("No key or auth alg given! Bug!") + + we_cipher = None + if len(key) == 26: + we_cipher = IW_AUTH_CIPHER_WEP104 + elif len(key) == 10: + we_cipher = IW_AUTH_CIPHER_WEP40 + else: + raise RuntimeError("Invalid key length!") + + # Stuff the returned key and auth algorithm into a security object + # and return it to NetworkManager + sec = Security.new_from_args(we_cipher, key, auth_alg) + async_cb(tuple(sec.get_properties())) + + def cancel_get_key_for_network(self): + pass