def _on_authorize_service(self, device, uuid, ok, err): def on_auth_action(action): logging.info(action) if action == "always": device = bluez.Device(n._device) device.set("Trusted", True) if action == "always" or action == "accept": ok() else: err(BluezErrorRejected("Rejected")) self.n = None logging.info("Agent.Authorize") dev_str = self.get_device_string(device) service = ServiceUUID(uuid).name notify_message = \ (_("Authorization request for:") + "\n%s\n" + _("Service:") + " <b>%s</b>") % (dev_str, service) actions = [["always", _("Always accept")], ["accept", _("Accept")], ["deny", _("Deny")]] n = Notification(_("Bluetooth Authentication"), notify_message, 0, actions, on_auth_action, icon_name="blueman") n.show() n._device = device
def _on_authorize_service(self, parameters, invocation): def on_auth_action(n, action): dprint(action) #self.applet.status_icon.set_blinking(False) if action == "always": device = Bluez.Device(n._device) device.set("Trusted", True) if action == "always" or action == "accept": invocation.return_value(GLib.Variant('()', ())) else: invocation.return_dbus_error('org.bluez.Error.Rejected', 'Rejected') self.n = None device, uuid = parameters.unpack() dprint("Agent.Authorize") alias = self.get_device_alias(device) uuid16 = uuid128_to_uuid16(uuid) service = uuid16_to_name(uuid16) notify_message = (_("Authorization request for:") + "\n%s\n" + _("Service:") + " <b>%s</b>") % (alias, service) actions = [["always", _("Always accept")], ["accept", _("Accept")], ["deny", _("Deny")]] n = Notification(_("Bluetooth Authentication"), notify_message, 0, actions, on_auth_action, pixbuf=get_icon("blueman", 48), status_icon=self.status_icon) n._device = device
def _on_authorize_service(self, parameters, invocation): def on_auth_action(action): logging.info(action) if action == "always": device = Bluez.Device(n._device) device.set("Trusted", True) if action == "always" or action == "accept": invocation.return_value(GLib.Variant('()', ())) else: invocation.return_dbus_error('org.bluez.Error.Rejected', 'Rejected') self.n = None device, uuid = parameters.unpack() logging.info("Agent.Authorize") dev_str = self.get_device_string(device) service = ServiceUUID(uuid).name notify_message = (_("Authorization request for:") + "\n%s\n" + _("Service:") + " <b>%s</b>") % (dev_str, service) actions = [["always", _("Always accept")], ["accept", _("Accept")], ["deny", _("Deny")]] n = Notification(_("Bluetooth Authentication"), notify_message, 0, actions, on_auth_action, icon_name="blueman", pos_hint=self.status_icon.geometry) n.show() n._device = device
def Authorize(self, device, uuid, ok, err): def on_auth_action(n, action): dprint(action) #self.applet.status_icon.set_blinking(False) if action == "always": device = Bluez.Device(n._device) device.set("Trusted", True) if action == "always" or action == "accept": ok() else: err(AgentErrorRejected()) self.n = None dprint("Agent.Authorize") alias = self.get_device_alias(device) uuid16 = uuid128_to_uuid16(uuid) service = uuid16_to_name(uuid16) notify_message = (_("Authorization request for:") + "\n%s\n" + _("Service:") + " <b>%s</b>") % (alias, service) actions = [["always", _("Always accept"), "blueman-trust"], ["accept", _("Accept"), "help-about"], ["deny", _("Deny"), "help-about"]] n = Notification(_("Bluetooth Authentication"), notify_message, 0, actions, on_auth_action, pixbuf=get_icon("blueman", 48), status_icon=self.status_icon) n._device = device
def _on_display_passkey(self, device, passkey, _entered): logging.info('DisplayPasskey (%s, %d)' % (device, passkey)) dev = Device(device) dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing passkey for") + " %s: %s" % ( self.get_device_string(device), passkey) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman") self.n.show()
def _on_display_pin_code(self, device: str, pin_code: str) -> None: logging.info(f'DisplayPinCode ({device}, {pin_code})') dev = Device(obj_path=device) self._devhandlerids[device] = dev.connect_signal( "property-changed", self._on_device_property_changed) notify_message = _("Pairing PIN code for" ) + f" {self.get_device_string(device)}: {pin_code}" self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman") self.n.show()
def _on_display_passkey(self, device, passkey, _entered): logging.info(f"DisplayPasskey ({device}, {passkey:d})") dev = Device(obj_path=device) self._devhandlerids[device] = dev.connect_signal( "property-changed", self._on_device_property_changed) notify_message = _("Pairing passkey for" ) + f" {self.get_device_string(device)}: {passkey}" self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman") self.n.show()
def _on_authorize(self, _agent, transfer_path, address=None, filename=None, size=None): if address and filename and size: # stand-alone obexd # FIXME: /tmp is only the default. Can we get the actual root # directory from stand-alone obexd? root = '/tmp' else: # BlueZ 5 integrated obexd transfer = obex.Transfer(transfer_path) session = obex.Session(transfer.session) root = session.root address = session.address filename = transfer.name size = transfer.size try: device = Device(self._applet.Manager.get_adapter().find_device(address)) name = device.Alias trusted = device.Trusted except Exception as e: dprint(e) name = address trusted = False self._pending_transfer = {'transfer_path': transfer_path, 'address': address, 'root': root, 'filename': filename, 'size': size, 'name': name} try: status_icon = self._applet.Plugins.StatusIcon except: status_icon = None # This device was neither allowed nor is it trusted -> ask for confirmation if address not in self._allowed_devices and not (self._config['opp-accept'] and trusted): self._notification = Notification(_("Incoming file over Bluetooth"), _("Incoming file %(0)s from %(1)s") % {"0": "<b>" + filename + "</b>", "1": "<b>" + name + "</b>"}, 30000, [["accept", _("Accept"), "help-about"], ["reject", _("Reject"), "help-about"]], self._on_action, pixbuf=get_icon("blueman", 48), status_icon=status_icon) # Device is trusted or was already allowed, larger file -> display a notification, but auto-accept elif size > 350000: self._notification = Notification(_("Receiving file"), _("Receiving file %(0)s from %(1)s") % {"0": "<b>" + filename + "</b>", "1": "<b>" + name + "</b>"}, pixbuf=get_icon("blueman", 48), status_icon=status_icon) self._on_action(self._notification, 'accept') # Device is trusted or was already allowed. very small file -> auto-accept and transfer silently else: self._notification = None self._on_action(self._notification, "accept")
def _on_display_passkey(self, parameters, invocation): device, passkey, entered = parameters.unpack() logging.info('DisplayPasskey (%s, %d)' % (device, passkey)) dev = bluez.Device(device) self.signal_id = dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing passkey for") + " %s: %s" % ( self.get_device_string(device), passkey) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman") self.n.show()
def _on_transfer_completed(self, _manager, transfer_path, success): try: attributes = self._agent.transfers[transfer_path] except KeyError: # This is probably not an incoming transfer we authorized return src = attributes['path'] dest_dir = self._config["shared-path"] filename = os.path.basename(src) if os.path.exists(os.path.join(dest_dir, filename)): now = datetime.now() filename = "%s_%s" % (now.strftime("%Y%m%d%H%M%S"), filename) dprint("Destination file exists, renaming to: %s" % filename) dest = os.path.join(dest_dir, filename) shutil.move(src, dest) try: status_icon = self._applet.Plugins.StatusIcon except: status_icon = None if success: n = Notification(_("File received"), _("File %(0)s from %(1)s successfully received") % { "0": "<b>" + filename + "</b>", "1": "<b>" + attributes['name'] + "</b>" }, pixbuf=get_icon("blueman", 48), status_icon=status_icon) self._add_open(n, "Open", dest) elif not success: Notification(_("Transfer failed"), _("Transfer of file %(0)s failed") % { "0": "<b>" + filename + "</b>", "1": "<b>" + attributes['name'] + "</b>" }, pixbuf=get_icon("blueman", 48), status_icon=status_icon) if attributes['size'] > 350000: self._normal_transfers -= 1 else: self._silent_transfers -= 1 del self._agent.transfers[transfer_path]
def _on_request_confirmation(self, parameters, invocation): def on_confirm_action(n, action): if action == "confirm": invocation.return_value(GLib.Variant('()', ())) else: invocation.return_dbus_error('org.bluez.Error.Canceled', "User canceled pairing") params = parameters.unpack() if len(params) < 2: device_path = params[0] passkey = None else: device_path, passkey = params dprint("Agent.RequestConfirmation") alias = self.get_device_alias(device_path) notify_message = _("Pairing request for:") + "\n%s" % alias if passkey: notify_message += "\n" + _( "Confirm value for authentication:") + " <b>%s</b>" % passkey actions = [["confirm", _("Confirm")], ["deny", _("Deny")]] self.n = Notification("Bluetooth", notify_message, 0, actions, on_confirm_action, pixbuf=get_icon("blueman", 48), status_icon=self.status_icon)
def reply(*args): label_text = item["mitem"].get_child().get_children()[1].get_text() Notification(_("Connected"), _("Connected to %s") % label_text, pixbuf=get_icon(item["icon"], 48), status_icon=self.Applet.Plugins.StatusIcon) item["mitem"].props.sensitive = True
def on_device_property_changed(self, path: str, key: str, value: Any) -> None: if key == "ServicesResolved" and value: device = Device(obj_path=path) if self.applicable(device): text = "%d%%" % BluezBattery(obj_path=path)["Percentage"] Notification(device["Alias"], text, icon_name="battery").show()
def reply(_obj, result, _user_data): logging.info(result) Notification(_("Bluetooth Network"), _("Interface %(0)s bound to IP address %(1)s") % {"0": device, "1": result}, icon_name="network-workgroup", pos_hint=self.Applet.Plugins.StatusIcon.geometry).show() self.quering.remove(device)
def _on_display_passkey(self, device: str, passkey: int, entered: int) -> None: logging.info(f"DisplayPasskey ({device}, {passkey:d} {entered:d})") dev = Device(obj_path=device) self._devhandlerids[device] = dev.connect_signal( "property-changed", self._on_device_property_changed) key = f"{passkey:06}" notify_message = _("Pairing passkey for") + f" {self.get_device_string(device)}: " \ f"{key[:entered]}<b>{key[entered]}</b>{key[entered+1:]}" self._close() self._notification = Notification("Bluetooth", notify_message, 0, icon_name="blueman") self._notification.show()
def ask_passkey( self, dialog_msg: str, is_numeric: bool, device_path: str, ok: Callable[[Any], None], err: Callable[[Union[BluezErrorCanceled, BluezErrorRejected]], None] ) -> None: def passkey_dialog_cb(dialog: Gtk.Dialog, response_id: int) -> None: if response_id == Gtk.ResponseType.ACCEPT: ret = pin_entry.get_text() ok(int(ret) if is_numeric else ret) else: err(BluezErrorRejected("Rejected")) dialog.destroy() self.dialog = None dev_str = self.get_device_string(device_path) notify_message = _("Pairing request for %s") % dev_str if self.dialog: logging.info("Agent: Another dialog still active, cancelling") err(BluezErrorCanceled("Canceled")) self.dialog, pin_entry = self.build_passkey_dialog( dev_str, dialog_msg, is_numeric) if not self.dialog: logging.error("Agent: Failed to build dialog") err(BluezErrorCanceled("Canceled")) Notification(_("Bluetooth Authentication"), notify_message, icon_name="blueman").show() self.dialog.connect("response", passkey_dialog_cb) self.dialog.present()
def dhcp_acquire(self, device: str) -> None: if device not in self.quering: self.quering.append(device) else: return if device != "": def reply(_obj: Mechanism, result: str, _user_data: None) -> None: logging.info(result) Notification(_("Bluetooth Network"), _("Interface %(0)s bound to IP address %(1)s") % {"0": device, "1": result}, icon_name="network-workgroup").show() self.quering.remove(device) def err(_obj: Mechanism, result: GLib.Error, _user_data: None) -> None: logging.warning(result) Notification(_("Bluetooth Network"), _("Failed to obtain an IP address on %s") % device, icon_name="network-workgroup").show() self.quering.remove(device) Notification(_("Bluetooth Network"), _("Trying to obtain an IP address on %s\nPlease wait…" % device), icon_name="network-workgroup").show() m = Mechanism() m.DhcpClient('(s)', device, result_handler=reply, error_handler=err, timeout=120 * 1000)
def dhcp_acquire(self, device): if device not in self.quering: self.quering.append(device) else: return if device != "": def reply(_obj, result, _user_data): logging.info(result) Notification(_("Bluetooth Network"), _("Interface %(0)s bound to IP address %(1)s") % {"0": device, "1": result}, icon_name="network-workgroup", pos_hint=self.Applet.Plugins.StatusIcon.geometry).show() self.quering.remove(device) def err(_obj, result, _user_data): logging.warning(result) Notification(_("Bluetooth Network"), _("Failed to obtain an IP address on %s") % (device), icon_name="network-workgroup", pos_hint=self.Applet.Plugins.StatusIcon.geometry).show() self.quering.remove(device) Notification(_("Bluetooth Network"), _("Trying to obtain an IP address on %s\nPlease wait..." % device), icon_name="network-workgroup", pos_hint=self.Applet.Plugins.StatusIcon.geometry).show() m = Mechanism() m.DhcpClient(str('(s)'), device, result_handler=reply, error_handler=err, timeout=120)
def reply(_obj: Mechanism, result: str, _user_data: None) -> None: logging.info(result) Notification(_("Bluetooth Network"), _("Interface %(0)s bound to IP address %(1)s") % {"0": device, "1": result}, icon_name="network-workgroup").show() self.quering.remove(device)
def _on_request_confirmation(self, device_path, passkey, ok, err): def on_confirm_action(action): if action == "confirm": ok() else: err(BluezErrorCanceled("User canceled pairing")) logging.info("Agent.RequestConfirmation") notify_message = _("Pairing request for:") + "\n%s" % self.get_device_string(device_path) if passkey: notify_message += "\n" + _("Confirm value for authentication:") + " <b>%s</b>" % passkey actions = [["confirm", _("Confirm")], ["deny", _("Deny")]] self.n = Notification("Bluetooth", notify_message, 0, actions, on_confirm_action, icon_name="blueman") self.n.show()
def err(reason): Notification( _("Failed to connect"), str(reason).split(": ")[-1], icon_name="dialog-error", pos_hint=self.Applet.Plugins.StatusIcon.geometry).show() menu_item.props.sensitive = True
def ask_passkey(self, dialog_msg, notify_msg, is_numeric, notification, device_path, ok, err): def passkey_dialog_cb(dialog, response_id): if response_id == Gtk.ResponseType.ACCEPT: ret = pin_entry.get_text() if is_numeric: ret = GLib.Variant('(u)', int(ret)) ok(ret) else: err(BluezErrorRejected("Rejected")) dialog.destroy() self.dialog = None dev_str = self.get_device_string(device_path) notify_message = _("Pairing request for %s") % dev_str if self.dialog: logging.info("Agent: Another dialog still active, cancelling") err(BluezErrorCanceled("Canceled")) self.dialog, pin_entry = self.build_passkey_dialog( dev_str, dialog_msg, is_numeric) if not self.dialog: logging.error("Agent: Failed to build dialog") err(BluezErrorCanceled("Canceled")) if notification: Notification(_("Bluetooth Authentication"), notify_message, icon_name="blueman").show() self.dialog.connect("response", passkey_dialog_cb) self.dialog.present()
def _on_display_pin_code(self, parameters, invocation): device, pin_code = parameters.unpack() logging.info('DisplayPinCode (%s, %s)' % (device, pin_code)) dev = bluez.Device(device) self.signal_id = dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing PIN code for") + " %s: %s" % ( self.get_device_string(device), pin_code) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman") self.n.show() invocation.return_value(None)
def on_mm_device_added(self, path): dprint(path) props = self.parent.bus.call_blocking( "org.freedesktop.ModemManager", path, "org.freedesktop.DBus.Properties", "GetAll", "s", ["org.freedesktop.ModemManager.Modem"]) if self.rfcomm_dev and props["Driver"] == "bluetooth" and props[ "Device"] in self.rfcomm_dev: dprint("It's our bluetooth modem!") modem = get_icon("modem", 24) blueman = get_icon("blueman", 48) icon = composite_icon(blueman, [(modem, 24, 24, 255)]) Notification( _("Bluetooth Dialup"), _("DUN connection on %s will now be available in Network Manager" ) % self.device.Alias, pixbuf=icon, status_icon=self.parent.Applet.Plugins.StatusIcon) self.reply(self.rfcomm_dev) self.cleanup()
def err(reason): Notification(_("Failed to connect"), str(reason).split(": ")[-1], pixbuf=get_icon("dialog-error", 48), status_icon=self.Applet.Plugins.StatusIcon) item["mitem"].props.sensitive = True sn.complete()
def on_mm_device_added(self, path, name="org.freedesktop.ModemManager"): dprint(path) interface = "%s.Modem" % name props = self.parent.bus.call_blocking( name, path, "org.freedesktop.DBus.Properties", "GetAll", "s", [interface]) try: drivers = props["Drivers"] except KeyError: drivers = [props["Driver"]] if self.rfcomm_dev and "bluetooth" in drivers and props[ "Device"] in self.rfcomm_dev: dprint("It's our bluetooth modem!") modem = get_icon("modem", 24) blueman = get_icon("blueman", 48) icon = composite_icon(blueman, [(modem, 24, 24, 255)]) Notification( _("Bluetooth Dialup"), _("DUN connection on %s will now be available in Network Manager" ) % self.service.device['Alias'], pixbuf=icon, status_icon=self.parent.Applet.Plugins.StatusIcon) self.reply(self.rfcomm_dev) self.cleanup()
def on_connected(self, _obj, result, _user_data): self.reply_handler(self.port) self.parent.Plugins.run("on_ppp_connected", self.service.device, self.port, result) msg = _("Successfully connected to <b>DUN</b> service on <b>%(0)s.</b>\n" "Network is now available through <b>%(1)s</b>") % {"0": self.service.device['Alias'], "1": result} Notification(_("Connected"), msg, icon_name="network-wireless").show()
def _on_display_pin_code(self, device, pin_code): logging.info('DisplayPinCode (%s, %s)' % (device, pin_code)) dev = bluez.Device(device) self.signal_id = dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing PIN code for") + " %s: %s" % (self.get_device_string(device), pin_code) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman") self.n.show()
def _on_display_passkey(self, device, passkey, _entered): logging.info('DisplayPasskey (%s, %d)' % (device, passkey)) dev = bluez.Device(device) self.signal_id = dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing passkey for") + " %s: %s" % (self.get_device_string(device), passkey) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman") self.n.show()
def err(reason: Union[Exception, str]) -> None: Notification(_("Failed to connect"), str(reason).split(": ")[-1], icon_name="dialog-error").show() assert item[ "mitem"] is not None # https://github.com/python/mypy/issues/2608 item["mitem"]["sensitive"] = True self.parent.Plugins.Menu.on_menu_changed()
def reply() -> None: assert item[ "mitem"] is not None # https://github.com/python/mypy/issues/2608 Notification(_("Connected"), _("Connected to %s") % item["mitem"]["text"], icon_name=item["icon"]).show() item["mitem"]["sensitive"] = True self.parent.Plugins.Menu.on_menu_changed()
def reply(*args): label_text = menu_item.get_child().get_children()[1].get_text() Notification( _("Connected"), _("Connected to %s") % label_text, icon_name=item["icon"], pos_hint=self.Applet.Plugins.StatusIcon.geometry).show() menu_item.props.sensitive = True
def on_device_property_changed(self, path: str, key: str, value: Any) -> None: if key == "Connected": device = Device(obj_path=path) if value and "Battery" in self.parent.Plugins.get_loaded() and Battery.applicable(device): return Notification(device["Alias"], _("Connected") if value else _("Disconnected"), icon_name=device["Icon"]).show()
def err(_obj, result, _user_data): logging.warning(result) Notification(_("Bluetooth Network"), _("Failed to obtain an IP address on %s") % device, icon_name="network-workgroup").show() self.quering.remove(device)
def _on_transfer_completed(self, _manager, transfer_path, success): try: attributes = self._agent.transfers[transfer_path] except KeyError: logging.info("This is probably not an incoming transfer we authorized") return src = attributes['path'] dest_dir, ignored = self._make_share_path() filename = os.path.basename(src) dest = os.path.join(dest_dir, filename) if os.path.exists(dest): now = datetime.now() filename = "%s_%s" % (now.strftime("%Y%m%d%H%M%S"), filename) logging.info("Destination file exists, renaming to: %s" % filename) try: shutil.move(src, dest) except (OSError, PermissionError): logging.error("Failed to move files", exc_info=True) success = False if success: self._notification = Notification(_("File received"), _("File %(0)s from %(1)s successfully received") % { "0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(attributes['name']) + "</b>"}, **self._notify_kwargs) self._add_open(self._notification, "Open", dest) self._notification.show() elif not success: n = Notification( _("Transfer failed"), _("Transfer of file %(0)s failed") % { "0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(attributes['name']) + "</b>"}, **self._notify_kwargs ) n.show() if attributes['size'] > 350000: self._normal_transfers -= 1 else: self._silent_transfers -= 1 del self._agent.transfers[transfer_path]
def reply(*args): Notification(_("Connected"), _("Connected to %s") % item["mitem"].get_child().props.label, pixbuf=get_icon("network-transmit-recieve", 48), status_icon=self.Applet.Plugins.StatusIcon) item["mitem"].props.sensitive = True sn.complete()
def DisplayPinCode(self, device, pin_code): dprint('DisplayPinCode (%s, %s)' % (device, pin_code)) notify_message = _("Pairing PIN code for") + " %s: %s" % ( self.get_device_alias(device), pin_code) self.n = Notification("Bluetooth", notify_message, 0, pixbuf=get_icon("blueman", 48), status_icon=self.status_icon)
def add_open(self, n, name, path): if Notification.actions_supported(): print "adding action" def on_open(*args): print "open" spawn(["xdg-open", path], True) n.add_action("open", name, on_open) n.show()
def _on_display_passkey(self, parameters, invocation): device, passkey, entered = parameters.unpack() logging.info('DisplayPasskey (%s, %d)' % (device, passkey)) dev = Bluez.Device(device) self.signal_id = dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing passkey for") + " %s: %s" % (self.get_device_string(device), passkey) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman", pos_hint=self.status_icon.geometry) self.n.show()
def err(msg): dprint(msg) Notification(_("Bluetooth Network"), _("Failed to obtain an IP address on %s") % (device), pixbuf=get_icon("gtk-network", 48), status_icon=self.Applet.Plugins.StatusIcon) self.quering.remove(device)
def _add_open(n, name, path): if Notification.actions_supported(): print("adding action") def on_open(*_args): print("open") launch("xdg-open", [path], True) n.add_action("open", name, on_open, None) n.show()
def _on_display_pin_code(self, parameters, invocation): device, pin_code = parameters.unpack() logging.info('DisplayPinCode (%s, %s)' % (device, pin_code)) dev = Bluez.Device(device) self.signal_id = dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing PIN code for") + " %s: %s" % (self.get_device_string(device), pin_code) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman", pos_hint=self.status_icon.geometry) self.n.show() invocation.return_value(None)
def _on_session_removed(self, _manager, _session_path): if self._silent_transfers == 0: return share_path, ignored = self._make_share_path() if self._normal_transfers == 0: self._notification = Notification(_("Files received"), ngettext("Received %d file in the background", "Received %d files in the background", self._silent_transfers) % self._silent_transfers, **self._notify_kwargs) self._add_open(self._notification, "Open Location", share_path) self._notification.show() else: self._notification = Notification(_("Files received"), ngettext("Received %d more file in the background", "Received %d more files in the background", self._silent_transfers) % self._silent_transfers, **self._notify_kwargs) self._add_open(self._notification, "Open Location", share_path) self._notification.show()
def on_load(self): def on_reset(*_args): self._notification = None self._config.reset('shared-path') logging.info('Reset share path') self._config = Config("org.blueman.transfer") share_path, invalid_share_path = self._make_share_path() if invalid_share_path: text = _('Configured directory for incoming files does not exist') secondary_text = _('Please make sure that directory "<b>%s</b>" exists or ' 'configure it with blueman-services. Until then the default "%s" will be used') self._notification = Notification(text, secondary_text % (self._config["shared-path"], share_path), icon_name='blueman', timeout=30000, actions=[['reset', 'Reset to default', 'blueman']], actions_cb=on_reset) self._notification.show() self._watch = obex.Manager.watch_name_owner(self._on_dbus_name_appeared, self._on_dbus_name_vanished)
def _on_request_confirmation(self, parameters, invocation): def on_confirm_action(action): if action == "confirm": invocation.return_value(GLib.Variant('()', ())) else: invocation.return_dbus_error('org.bluez.Error.Canceled', "User canceled pairing") params = parameters.unpack() if len(params) < 2: device_path = params[0] passkey = None else: device_path, passkey = params logging.info("Agent.RequestConfirmation") notify_message = _("Pairing request for:") + "\n%s" % self.get_device_string(device_path) if passkey: notify_message += "\n" + _("Confirm value for authentication:") + " <b>%s</b>" % passkey actions = [["confirm", _("Confirm")], ["deny", _("Deny")]] self.n = Notification("Bluetooth", notify_message, 0, actions, on_confirm_action, icon_name="blueman") self.n.show()
def _on_session_removed(self, _manager, _session_path): if self._silent_transfers == 0: return if self._normal_transfers == 0: n = Notification(_("Files received"), ngettext("Received %d file in the background", "Received %d files in the background", self._silent_transfers) % self._silent_transfers, **self._notify_kwargs) self._add_open(n, "Open Location", self._config["shared-path"]) n.show() else: n = Notification(_("Files received"), ngettext("Received %d more file in the background", "Received %d more files in the background", self._silent_transfers) % self._silent_transfers, **self._notify_kwargs) self._add_open(n, "Open Location", self._config["shared-path"]) n.show()
def transfer_finished(self, session, *args): type = args[-1] dprint(args) try: if not session.transfer["finished"]: if type != "cancelled" and type != "error": session.transfer["finished"] = True if session.transfer["total"] > 350000: icon = get_icon("blueman", 48) n = Notification(_("File received"), _("File %(0)s from %(1)s successfully received") % { "0": "<b>" + session.transfer["filename"] + "</b>", "1": "<b>" + session.transfer["name"] + "</b>"}, pixbuf=icon, status_icon=self.status_icon) self.add_open(n, "Open", session.transfer["filepath"]) else: session.transfer["failed"] = True session.transfer["finished"] = True n = session.transfer["notification"] if n: n.close() icon = get_icon("blueman", 48) session.transfer["notification"] = Notification(_("Transfer failed"), _("Transfer of file %(0)s failed") % { "0": "<b>" + session.transfer["filename"] + "</b>", "1": "<b>" + session.transfer["name"] + "</b>"}, pixbuf=icon, status_icon=self.status_icon) if session.transfer["total"] > 350000: session.transfer["normal_transfers"] -= 1 else: session.transfer["silent_transfers"] -= 1 if type == "disconnected": icon = get_icon("blueman", 48) if session.transfer["normal_transfers"] == 0 and session.transfer["silent_transfers"] == 1: n = Notification(_("File received"), _("File %(0)s from %(1)s successfully received") % { "0": "<b>" + session.transfer["filename"] + "</b>", "1": "<b>" + session.transfer["name"] + "</b>"}, pixbuf=icon, status_icon=self.status_icon) self.add_open(n, "Open", session.transfer["filepath"]) elif session.transfer["normal_transfers"] == 0 and session.transfer["silent_transfers"] > 0: n = Notification(_("Files received"), ngettext("Received %d file in the background", "Received %d files in the background", session.transfer["silent_transfers"]) % session.transfer[ "silent_transfers"], pixbuf=icon, status_icon=self.status_icon) self.add_open(n, "Open Location", self.Config.props.shared_path) elif session.transfer["normal_transfers"] > 0 and session.transfer["silent_transfers"] > 0: n = Notification(_("Files received"), ngettext("Received %d more file in the background", "Received %d more files in the background", session.transfer["silent_transfers"]) % session.transfer[ "silent_transfers"], pixbuf=icon, status_icon=self.status_icon) self.add_open(n, "Open Location", self.Config.props.shared_path) del session.transfer del session.server except KeyError: pass
class TransferService(AppletPlugin): __author__ = "cschramm" __description__ = _("Provides OBEX file transfer capabilities") __icon__ = "blueman-send-file" _config = None _silent_transfers = 0 _normal_transfers = 0 _manager = None _signals = [] _agent = None _watch = None _notification = None def on_load(self): def on_reset(*_args): self._notification = None self._config.reset('shared-path') logging.info('Reset share path') self._config = Config("org.blueman.transfer") share_path, invalid_share_path = self._make_share_path() if invalid_share_path: text = _('Configured directory for incoming files does not exist') secondary_text = _('Please make sure that directory "<b>%s</b>" exists or ' 'configure it with blueman-services. Until then the default "%s" will be used') self._notification = Notification(text, secondary_text % (self._config["shared-path"], share_path), icon_name='blueman', timeout=30000, actions=[['reset', 'Reset to default', 'blueman']], actions_cb=on_reset) self._notification.show() self._watch = obex.Manager.watch_name_owner(self._on_dbus_name_appeared, self._on_dbus_name_vanished) def on_unload(self): if self._watch: Gio.bus_unwatch_name(self._watch) self._unregister_agent() def _make_share_path(self): config_path = self._config["shared-path"] default_path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOWNLOAD) path = None error = False if config_path == '': path = default_path elif not os.path.isdir(config_path): path = default_path error = True logging.warning('Invalid shared-path %s' % config_path) else: path = config_path if not path: path = os.path.expanduser("~") logging.warning('Failed to get Download dir from XDG') # We used to always store the full path which caused problems if config_path == default_path: logging.info('Reset stored path, identical to default path.') self._config["shared-path"] = '' return path, error def _register_agent(self): if not self._agent: self._agent = Agent(self.parent) self._agent.register_at_manager() def _unregister_agent(self): if self._agent: self._agent.unregister_from_manager() self._agent.unregister() self._agent = None def _on_dbus_name_appeared(self, _connection, name, owner): logging.info("%s %s" % (name, owner)) self._manager = obex.Manager() self._signals.append(self._manager.connect("transfer-started", self._on_transfer_started)) self._signals.append(self._manager.connect("transfer-completed", self._on_transfer_completed)) self._signals.append(self._manager.connect('session-removed', self._on_session_removed)) self._register_agent() def _on_dbus_name_vanished(self, _connection, name): logging.info("%s not running or was stopped" % name) for signal in self._signals: self._manager.disconnect(signal) self._manager = None if self._agent: self._agent.unregister() self._agent = None def _on_transfer_started(self, _manager, transfer_path): if transfer_path not in self._agent.transfers: # This is not an incoming transfer we authorized return if self._agent.transfers[transfer_path]['size'] > 350000: self._normal_transfers += 1 else: self._silent_transfers += 1 def _add_open(self, n, name, path): if n.actions_supported: logging.info("adding action") def on_open(*_args): self._notification = None logging.info("open") launch("xdg-open", [path], True) n.add_action("open", name, on_open) @property def _notify_kwargs(self): kwargs = {"icon_name": "blueman"} try: kwargs["pos_hint"] = self.parent.Plugins.StatusIcon.geometry except AttributeError: logging.error("No statusicon found") return kwargs def _on_transfer_completed(self, _manager, transfer_path, success): try: attributes = self._agent.transfers[transfer_path] except KeyError: logging.info("This is probably not an incoming transfer we authorized") return src = attributes['path'] dest_dir, ignored = self._make_share_path() filename = os.path.basename(src) dest = os.path.join(dest_dir, filename) if os.path.exists(dest): now = datetime.now() filename = "%s_%s" % (now.strftime("%Y%m%d%H%M%S"), filename) logging.info("Destination file exists, renaming to: %s" % filename) try: shutil.move(src, dest) except (OSError, PermissionError): logging.error("Failed to move files", exc_info=True) success = False if success: self._notification = Notification(_("File received"), _("File %(0)s from %(1)s successfully received") % { "0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(attributes['name']) + "</b>"}, **self._notify_kwargs) self._add_open(self._notification, "Open", dest) self._notification.show() elif not success: n = Notification( _("Transfer failed"), _("Transfer of file %(0)s failed") % { "0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(attributes['name']) + "</b>"}, **self._notify_kwargs ) n.show() if attributes['size'] > 350000: self._normal_transfers -= 1 else: self._silent_transfers -= 1 del self._agent.transfers[transfer_path] def _on_session_removed(self, _manager, _session_path): if self._silent_transfers == 0: return share_path, ignored = self._make_share_path() if self._normal_transfers == 0: self._notification = Notification(_("Files received"), ngettext("Received %d file in the background", "Received %d files in the background", self._silent_transfers) % self._silent_transfers, **self._notify_kwargs) self._add_open(self._notification, "Open Location", share_path) self._notification.show() else: self._notification = Notification(_("Files received"), ngettext("Received %d more file in the background", "Received %d more files in the background", self._silent_transfers) % self._silent_transfers, **self._notify_kwargs) self._add_open(self._notification, "Open Location", share_path) self._notification.show()
def _on_authorize(self, parameters, invocation): def on_action(_notification, action): dprint(action) if action == "accept": self.transfers[self._pending_transfer['transfer_path']] = { 'path': self._pending_transfer['root'] + '/' + os.path.basename(self._pending_transfer['filename']), 'size': self._pending_transfer['size'], 'name': self._pending_transfer['name'] } param = GLib.Variant('(s)', (self.transfers[self._pending_transfer['transfer_path']]['path'],)) invocation.return_value(param) self._allowed_devices.append(self._pending_transfer['address']) GLib.timeout_add(60000, self._allowed_devices.remove, self._pending_transfer['address']) else: invocation.return_dbus_error('org.bluez.obex.Error.Rejected', 'Rejected') transfer_path = parameters.unpack()[0] transfer = obex.Transfer(transfer_path) session = obex.Session(transfer.session) root = session.root[0] address = session.address[0] filename = transfer.name size = transfer.size try: device = self._applet.Manager.get_adapter().find_device(address) name = device["Alias"] trusted = device["Trusted"] except Exception as e: dprint(e) name = address trusted = False self._pending_transfer = {'transfer_path': transfer_path, 'address': address, 'root': root, 'filename': filename, 'size': size, 'name': name} try: status_icon = self._applet.Plugins.StatusIcon except: status_icon = None # This device was neither allowed nor is it trusted -> ask for confirmation if address not in self._allowed_devices and not (self._config['opp-accept'] and trusted): self._notification = Notification(_("Incoming file over Bluetooth"), _("Incoming file %(0)s from %(1)s") % {"0": "<b>" + filename + "</b>", "1": "<b>" + name + "</b>"}, 30000, [["accept", _("Accept"), "help-about"], ["reject", _("Reject"), "help-about"]], on_action, pixbuf=get_icon("blueman", 48), status_icon=status_icon) # Device is trusted or was already allowed, larger file -> display a notification, but auto-accept elif size > 350000: self._notification = Notification(_("Receiving file"), _("Receiving file %(0)s from %(1)s") % {"0": "<b>" + filename + "</b>", "1": "<b>" + name + "</b>"}, pixbuf=get_icon("blueman", 48), status_icon=status_icon) on_action(self._notification, 'accept') # Device is trusted or was already allowed. very small file -> auto-accept and transfer silently else: self._notification = None on_action(self._notification, "accept")
class Agent(DbusService): __agent_path = '/org/bluez/obex/agent/blueman' def __init__(self, applet): super().__init__(None, "org.bluez.obex.Agent1", self.__agent_path, Gio.BusType.SESSION) self.add_method("Release", (), "", self._release) self.add_method("Cancel", (), "", self._cancel) self.add_method("AuthorizePush", ("o",), "s", self._authorize_push, is_async=True) self.register() self._applet = applet self._config = Config("org.blueman.transfer") self._allowed_devices = [] self._notification = None self._pending_transfer = None self.transfers = {} def register_at_manager(self): obex.AgentManager().register_agent(self.__agent_path) def unregister_from_manager(self): obex.AgentManager().unregister_agent(self.__agent_path) def _release(self): raise Exception(self.__agent_path + " was released unexpectedly") def _authorize_push(self, transfer_path, ok, err): def on_action(action): logging.info("Action %s" % action) if action == "accept": self.transfers[self._pending_transfer['transfer_path']] = { 'path': self._pending_transfer['root'] + '/' + os.path.basename(self._pending_transfer['filename']), 'size': self._pending_transfer['size'], 'name': self._pending_transfer['name'] } ok(self.transfers[self._pending_transfer['transfer_path']]['path']) self._allowed_devices.append(self._pending_transfer['address']) GLib.timeout_add(60000, self._allowed_devices.remove, self._pending_transfer['address']) else: err(ObexErrorRejected("Rejected")) transfer = obex.Transfer(transfer_path) session = obex.Session(transfer.session) root = session.root address = session.address filename = transfer.name size = transfer.size try: adapter = self._applet.Manager.get_adapter() device = self._applet.Manager.find_device(address, adapter.get_object_path()) name = device["Alias"] trusted = device["Trusted"] except Exception as e: logging.exception(e) name = address trusted = False self._pending_transfer = {'transfer_path': transfer_path, 'address': address, 'root': root, 'filename': filename, 'size': size, 'name': name} notif_kwargs = {"icon_name": "blueman"} try: notif_kwargs["pos_hint"] = self._applet.Plugins.StatusIcon.geometry except AttributeError: logging.error("Failed to get StatusIcon") # This device was neither allowed nor is it trusted -> ask for confirmation if address not in self._allowed_devices and not (self._config['opp-accept'] and trusted): self._notification = Notification( _("Incoming file over Bluetooth"), _("Incoming file %(0)s from %(1)s") % {"0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(name) + "</b>"}, 30000, [["accept", _("Accept"), "help-about"], ["reject", _("Reject"), "help-about"]], on_action, **notif_kwargs ) self._notification.show() # Device is trusted or was already allowed, larger file -> display a notification, but auto-accept elif size > 350000: self._notification = Notification( _("Receiving file"), _("Receiving file %(0)s from %(1)s") % {"0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(name) + "</b>"}, **notif_kwargs ) on_action('accept') self._notification.show() # Device is trusted or was already allowed. very small file -> auto-accept and transfer silently else: self._notification = None on_action("accept") def _cancel(self): self._notification.close() raise ObexErrorCanceled("Canceled")
class _Agent: def __init__(self, applet): self._applet = applet self._config = Config("org.blueman.transfer") self._agent_path = "/org/blueman/obex_agent" self._agent = obex.Agent(self._agent_path) self._agent.connect("release", self._on_release) self._agent.connect("authorize", self._on_authorize) self._agent.connect("cancel", self._on_cancel) self._allowed_devices = [] self._notification = None self._pending_transfer = None self.transfers = {} obex.AgentManager().register_agent(self._agent_path) def __del__(self): obex.AgentManager().unregister_agent(self._agent_path) def _on_release(self, _agent): raise Exception(self._agent_path + " was released unexpectedly") def _on_action(self, _notification, action): dprint(action) if action == "accept": self.transfers[self._pending_transfer["transfer_path"]] = { "path": self._pending_transfer["root"] + "/" + os.path.basename(self._pending_transfer["filename"]), "size": self._pending_transfer["size"], "name": self._pending_transfer["name"], } self._agent.reply(self.transfers[self._pending_transfer["transfer_path"]]["path"]) self._allowed_devices.append(self._pending_transfer["address"]) GObject.timeout_add(60000, self._allowed_devices.remove, self._pending_transfer["address"]) else: self._agent.reply(obex.Error.Rejected) def _on_authorize(self, _agent, transfer_path, address=None, filename=None, size=None): if address and filename and size: # stand-alone obexd # FIXME: /tmp is only the default. Can we get the actual root # directory from stand-alone obexd? root = "/tmp" else: # BlueZ 5 integrated obexd transfer = obex.Transfer(transfer_path) session = obex.Session(transfer.session) root = session.root address = session.address filename = transfer.name size = transfer.size try: device = Device(self._applet.Manager.get_adapter().find_device(address)) name = device.Alias trusted = device.Trusted except Exception as e: dprint(e) name = address trusted = False self._pending_transfer = { "transfer_path": transfer_path, "address": address, "root": root, "filename": filename, "size": size, "name": name, } try: status_icon = self._applet.Plugins.StatusIcon except: status_icon = None # This device was neither allowed nor is it trusted -> ask for confirmation if address not in self._allowed_devices and not (self._config["opp-accept"] and trusted): self._notification = Notification( _("Incoming file over Bluetooth"), _("Incoming file %(0)s from %(1)s") % {"0": "<b>" + filename + "</b>", "1": "<b>" + name + "</b>"}, 30000, [["accept", _("Accept"), "help-about"], ["reject", _("Reject"), "help-about"]], self._on_action, pixbuf=get_icon("blueman", 48), status_icon=status_icon, ) # Device is trusted or was already allowed, larger file -> display a notification, but auto-accept elif size > 350000: self._notification = Notification( _("Receiving file"), _("Receiving file %(0)s from %(1)s") % {"0": "<b>" + filename + "</b>", "1": "<b>" + name + "</b>"}, pixbuf=get_icon("blueman", 48), status_icon=status_icon, ) self._on_action(self._notification, "accept") # Device is trusted or was already allowed. very small file -> auto-accept and transfer silently else: self._notification = None self._on_action(self._notification, "accept") def _on_cancel(self, agent): self._notification.close() agent.reply(obex.Error.Canceled)
def _on_authorize(self, parameters, invocation): def on_action(action): logging.info("Action %s" % action) if action == "accept": self.transfers[self._pending_transfer['transfer_path']] = { 'path': self._pending_transfer['root'] + '/' + os.path.basename(self._pending_transfer['filename']), 'size': self._pending_transfer['size'], 'name': self._pending_transfer['name'] } param = GLib.Variant('(s)', (self.transfers[self._pending_transfer['transfer_path']]['path'],)) invocation.return_value(param) self._allowed_devices.append(self._pending_transfer['address']) GLib.timeout_add(60000, self._allowed_devices.remove, self._pending_transfer['address']) else: invocation.return_dbus_error('org.bluez.obex.Error.Rejected', 'Rejected') transfer_path = parameters.unpack()[0] transfer = obex.Transfer(transfer_path) session = obex.Session(transfer.session) root = session.root address = session.address filename = transfer.name size = transfer.size try: adapter = self._applet.Manager.get_adapter() device = self._applet.Manager.find_device(address, adapter.get_object_path()) name = device["Alias"] trusted = device["Trusted"] except Exception as e: logging.exception(e) name = address trusted = False self._pending_transfer = {'transfer_path': transfer_path, 'address': address, 'root': root, 'filename': filename, 'size': size, 'name': name} notif_kwargs = {"icon_name": "blueman"} try: notif_kwargs["pos_hint"] = self._applet.Plugins.StatusIcon.geometry except AttributeError: logging.error("Failed to get StatusIcon") # This device was neither allowed nor is it trusted -> ask for confirmation if address not in self._allowed_devices and not (self._config['opp-accept'] and trusted): self._notification = Notification(_("Incoming file over Bluetooth"), _("Incoming file %(0)s from %(1)s") % {"0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(name) + "</b>"}, 30000, [["accept", _("Accept"), "help-about"], ["reject", _("Reject"), "help-about"]], on_action, **notif_kwargs) self._notification.show() # Device is trusted or was already allowed, larger file -> display a notification, but auto-accept elif size > 350000: self._notification = Notification(_("Receiving file"), _("Receiving file %(0)s from %(1)s") % {"0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(name) + "</b>"}, **notif_kwargs) on_action('accept') self._notification.show() # Device is trusted or was already allowed. very small file -> auto-accept and transfer silently else: self._notification = None on_action("accept")
class Agent(obex.Agent): __agent_path = '/org/bluez/obex/agent/blueman' def __init__(self, applet): super(Agent, self).__init__(self.__agent_path, self._handle_method_call) self._applet = applet self._config = Config("org.blueman.transfer") self._allowed_devices = [] self._notification = None self._pending_transfer = None self.transfers = {} def _handle_method_call(self, connection, sender, agent_path, interface_name, method_name, parameters, invocation): log_msg = "%s %s" % (method_name, agent_path) if method_name == 'Release': logging.info(log_msg) self._on_release() elif method_name == 'AuthorizePush': logging.info(log_msg) self._on_authorize(parameters, invocation) elif method_name == 'Cancel': logging.info(log_msg) self._on_cancel(parameters, invocation) def register(self): obex.AgentManager().register_agent(self.__agent_path) def unregister(self): obex.AgentManager().unregister_agent(self.__agent_path) def _on_release(self): raise Exception(self.__agent_path + " was released unexpectedly") def _on_authorize(self, parameters, invocation): def on_action(action): logging.info("Action %s" % action) if action == "accept": self.transfers[self._pending_transfer['transfer_path']] = { 'path': self._pending_transfer['root'] + '/' + os.path.basename(self._pending_transfer['filename']), 'size': self._pending_transfer['size'], 'name': self._pending_transfer['name'] } param = GLib.Variant('(s)', (self.transfers[self._pending_transfer['transfer_path']]['path'],)) invocation.return_value(param) self._allowed_devices.append(self._pending_transfer['address']) GLib.timeout_add(60000, self._allowed_devices.remove, self._pending_transfer['address']) else: invocation.return_dbus_error('org.bluez.obex.Error.Rejected', 'Rejected') transfer_path = parameters.unpack()[0] transfer = obex.Transfer(transfer_path) session = obex.Session(transfer.session) root = session.root address = session.address filename = transfer.name size = transfer.size try: adapter = self._applet.Manager.get_adapter() device = self._applet.Manager.find_device(address, adapter.get_object_path()) name = device["Alias"] trusted = device["Trusted"] except Exception as e: logging.exception(e) name = address trusted = False self._pending_transfer = {'transfer_path': transfer_path, 'address': address, 'root': root, 'filename': filename, 'size': size, 'name': name} notif_kwargs = {"icon_name": "blueman"} try: notif_kwargs["pos_hint"] = self._applet.Plugins.StatusIcon.geometry except AttributeError: logging.error("Failed to get StatusIcon") # This device was neither allowed nor is it trusted -> ask for confirmation if address not in self._allowed_devices and not (self._config['opp-accept'] and trusted): self._notification = Notification(_("Incoming file over Bluetooth"), _("Incoming file %(0)s from %(1)s") % {"0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(name) + "</b>"}, 30000, [["accept", _("Accept"), "help-about"], ["reject", _("Reject"), "help-about"]], on_action, **notif_kwargs) self._notification.show() # Device is trusted or was already allowed, larger file -> display a notification, but auto-accept elif size > 350000: self._notification = Notification(_("Receiving file"), _("Receiving file %(0)s from %(1)s") % {"0": "<b>" + escape(filename) + "</b>", "1": "<b>" + escape(name) + "</b>"}, **notif_kwargs) on_action('accept') self._notification.show() # Device is trusted or was already allowed. very small file -> auto-accept and transfer silently else: self._notification = None on_action("accept") def _on_cancel(self, parameters, invocation): self._notification.close() invocation.return_dbus_error('org.bluez.obex.Error.Canceled', 'Canceled')
class BluezAgent(Agent): __agent_path = '/org/bluez/agent/blueman' def __init__(self, status_icon, time_func): super(BluezAgent, self).__init__(self.__agent_path, self._handle_method_call) self.status_icon = status_icon self.dialog = None self.n = None self.signal_id = None self.time_func = time_func self._db = None def register_agent(self): logging.info("Register Agent") self._register_object() Bluez.AgentManager().register_agent(self.__agent_path, "KeyboardDisplay", default=True) def unregister_agent(self): logging.info("Unregister Agent") self._unregister_object() Bluez.AgentManager().unregister_agent(self.__agent_path) def _handle_method_call(self, connection, sender, agent_path, interface_name, method_name, parameters, invocation): if method_name == 'Release': self._on_release() elif method_name == 'RequestPinCode': self._on_request_pin_code(parameters, invocation) elif method_name == 'DisplayPinCode': self._on_display_pin_code(parameters, invocation) elif method_name == 'RequestPasskey': self._on_request_passkey(parameters, invocation) elif method_name == 'DisplayPasskey': self._on_display_passkey(parameters, invocation) elif method_name == 'RequestConfirmation': self._on_request_confirmation(parameters, invocation) elif method_name == 'RequestAuthorization': self._on_request_authorization(parameters, invocation) elif method_name == 'AuthorizeService': self._on_authorize_service(parameters, invocation) elif method_name == 'Cancel': self._on_cancel() else: logging.warning('Unhandled method: %s' % method_name) def build_passkey_dialog(self, device_alias, dialog_msg, is_numeric): def on_insert_text(editable, new_text, new_text_length, position): if not new_text.isdigit(): editable.stop_emission("insert-text") builder = Gtk.Builder() builder.add_from_file(UI_PATH + "/applet-passkey.ui") builder.set_translation_domain("blueman") bind_textdomain_codeset("blueman", "UTF-8") dialog = builder.get_object("dialog") dialog.props.icon_name = "blueman" dev_name = builder.get_object("device_name") dev_name.set_markup(device_alias) msg = builder.get_object("message") msg.set_text(dialog_msg) pin_entry = builder.get_object("pin_entry") show_input = builder.get_object("show_input_check") if (is_numeric): pin_entry.set_max_length(6) pin_entry.set_width_chars(6) pin_entry.connect("insert-text", on_insert_text) show_input.hide() else: pin_entry.set_max_length(16) pin_entry.set_width_chars(16) pin_entry.set_visibility(False) show_input.connect("toggled", lambda x: pin_entry.set_visibility(x.props.active)) accept_button = builder.get_object("accept") pin_entry.connect("changed", lambda x: accept_button.set_sensitive(x.get_text() != '')) return (dialog, pin_entry) def get_device_string(self, device_path): device = Bluez.Device(device_path) return "<b>%s</b> (%s)" % (escape(device["Alias"]), device["Address"]) def _lookup_default_pin(self, device_path): if not self._db: self._db = ElementTree.parse(os.path.join(PKGDATA_DIR, 'pin-code-database.xml')) device = Bluez.Device(device_path) lookup_dict = { 'name': device['Name'], 'type': bt_class_to_string(device['Class']), 'oui': device['Address'][:9] } pin = None for s in PIN_SEARCHES: search = s.format(**lookup_dict) entry = self._db.find(search) if entry is not None: pin = entry.get('pin') break if pin is not None: if 'max:' in pin: pin = "".join(random.sample('123456789', int(pin[-1]))) return pin def ask_passkey(self, dialog_msg, notify_msg, is_numeric, notification, parameters, invocation): device_path = parameters.unpack()[0] def passkey_dialog_cb(dialog, response_id): if response_id == Gtk.ResponseType.ACCEPT: ret = pin_entry.get_text() if is_numeric: ret = GLib.Variant('(u)', int(ret)) invocation.return_value(GLib.Variant('(s)', (ret,))) else: invocation.return_dbus_error('org.bluez.Error.Rejected', 'Rejected') dialog.destroy() self.dialog = None dev_str = self.get_device_string(device_path) notify_message = _("Pairing request for %s") % dev_str if self.dialog: logging.info("Agent: Another dialog still active, cancelling") invocation.return_dbus_error('org.bluez.Error.Canceled', 'Canceled') self.dialog, pin_entry = self.build_passkey_dialog(dev_str, dialog_msg, is_numeric) if not self.dialog: logging.error("Agent: Failed to build dialog") invocation.return_dbus_error('org.bluez.Error.Canceled', 'Canceled') if notification: Notification(_("Bluetooth Authentication"), notify_message, icon_name="blueman", pos_hint=self.status_icon.geometry) self.dialog.connect("response", passkey_dialog_cb) self.dialog.present() # Workaround BlueZ not calling the Cancel method, see #164 def _on_device_property_changed(self, device, key, value, path): if (key == "Paired" and value) or (key == "Connected" and not value): device.disconnect_signal(self.signal_id) self._on_cancel() def _on_release(self): logging.info("Agent.Release") self._on_cancel() self._unregister_object() def _on_cancel(self): logging.info("Agent.Cancel") if self.dialog: self.dialog.response(Gtk.ResponseType.REJECT) try: self.n.close() except AttributeError: pass def _on_request_pin_code(self, parameters, invocation): logging.info("Agent.RequestPinCode") dialog_msg = _("Enter PIN code for authentication:") notify_msg = _("Enter PIN code") object_path = parameters.unpack()[0] default_pin = self._lookup_default_pin(object_path) if default_pin is not None: logging.info('Sending default pin: %s' % default_pin) invocation.return_value(GLib.Variant('(s)', (default_pin,))) return self.ask_passkey(dialog_msg, notify_msg, False, True, parameters, invocation) if self.dialog: self.dialog.present_with_time(self.time_func()) def _on_request_passkey(self, parameters, invocation): logging.info("Agent.RequestPasskey") dialog_msg = _("Enter passkey for authentication:") notify_msg = _("Enter passkey") self.ask_passkey(dialog_msg, notify_msg, True, True, parameters, invocation) if self.dialog: self.dialog.present_with_time(self.time_func()) def _on_display_passkey(self, parameters, invocation): device, passkey, entered = parameters.unpack() logging.info('DisplayPasskey (%s, %d)' % (device, passkey)) dev = Bluez.Device(device) self.signal_id = dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing passkey for") + " %s: %s" % (self.get_device_string(device), passkey) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman", pos_hint=self.status_icon.geometry) self.n.show() def _on_display_pin_code(self, parameters, invocation): device, pin_code = parameters.unpack() logging.info('DisplayPinCode (%s, %s)' % (device, pin_code)) dev = Bluez.Device(device) self.signal_id = dev.connect_signal("property-changed", self._on_device_property_changed) notify_message = _("Pairing PIN code for") + " %s: %s" % (self.get_device_string(device), pin_code) self.n = Notification("Bluetooth", notify_message, 0, icon_name="blueman", pos_hint=self.status_icon.geometry) self.n.show() invocation.return_value(None) def _on_request_confirmation(self, parameters, invocation): def on_confirm_action(action): if action == "confirm": invocation.return_value(GLib.Variant('()', ())) else: invocation.return_dbus_error('org.bluez.Error.Canceled', "User canceled pairing") params = parameters.unpack() if len(params) < 2: device_path = params[0] passkey = None else: device_path, passkey = params logging.info("Agent.RequestConfirmation") notify_message = _("Pairing request for:") + "\n%s" % self.get_device_string(device_path) if passkey: notify_message += "\n" + _("Confirm value for authentication:") + " <b>%s</b>" % passkey actions = [["confirm", _("Confirm")], ["deny", _("Deny")]] self.n = Notification("Bluetooth", notify_message, 0, actions, on_confirm_action, icon_name="blueman", pos_hint=self.status_icon.geometry) self.n.show() def _on_request_authorization(self, parameters, invocation): self._on_request_confirmation(parameters, invocation) def _on_authorize_service(self, parameters, invocation): def on_auth_action(action): logging.info(action) if action == "always": device = Bluez.Device(n._device) device.set("Trusted", True) if action == "always" or action == "accept": invocation.return_value(GLib.Variant('()', ())) else: invocation.return_dbus_error('org.bluez.Error.Rejected', 'Rejected') self.n = None device, uuid = parameters.unpack() logging.info("Agent.Authorize") dev_str = self.get_device_string(device) service = ServiceUUID(uuid).name notify_message = (_("Authorization request for:") + "\n%s\n" + _("Service:") + " <b>%s</b>") % (dev_str, service) actions = [["always", _("Always accept")], ["accept", _("Accept")], ["deny", _("Deny")]] n = Notification(_("Bluetooth Authentication"), notify_message, 0, actions, on_auth_action, icon_name="blueman", pos_hint=self.status_icon.geometry) n.show() n._device = device
class _Agent: def __init__(self, applet): self._applet = applet self._config = Config("org.blueman.transfer") self._agent_path = '/org/blueman/obex_agent' self._agent = obex.Agent(self._agent_path) self._agent.connect('release', self._on_release) self._agent.connect('authorize', self._on_authorize) self._agent.connect('cancel', self._on_cancel) self._allowed_devices = [] self._notification = None self._pending_transfer = None self.transfers = {} def register(self): obex.AgentManager().register_agent(self._agent_path) def unregister(self): obex.AgentManager().unregister_agent(self._agent_path) def _on_release(self, _agent): raise Exception(self._agent_path + " was released unexpectedly") def _on_action(self, _notification, action): dprint(action) if action == "accept": self.transfers[self._pending_transfer['transfer_path']] = { 'path': self._pending_transfer['root'] + '/' + os.path.basename(self._pending_transfer['filename']), 'size': self._pending_transfer['size'], 'name': self._pending_transfer['name'] } self._agent.reply(self.transfers[self._pending_transfer['transfer_path']]['path']) self._allowed_devices.append(self._pending_transfer['address']) GLib.timeout_add(60000, self._allowed_devices.remove, self._pending_transfer['address']) else: self._agent.reply(obex.Error.Rejected) def _on_authorize(self, _agent, transfer_path): transfer = obex.Transfer(transfer_path) session = obex.Session(transfer.session) root = session.root address = session.address filename = transfer.name size = transfer.size try: device = self._applet.Manager.get_adapter().find_device(address) name = device["Alias"] trusted = device["Trusted"] except Exception as e: dprint(e) name = address trusted = False self._pending_transfer = {'transfer_path': transfer_path, 'address': address, 'root': root, 'filename': filename, 'size': size, 'name': name} try: status_icon = self._applet.Plugins.StatusIcon except: status_icon = None # This device was neither allowed nor is it trusted -> ask for confirmation if address not in self._allowed_devices and not (self._config['opp-accept'] and trusted): self._notification = Notification(_("Incoming file over Bluetooth"), _("Incoming file %(0)s from %(1)s") % {"0": "<b>" + filename + "</b>", "1": "<b>" + name + "</b>"}, 30000, [["accept", _("Accept"), "help-about"], ["reject", _("Reject"), "help-about"]], self._on_action, pixbuf=get_icon("blueman", 48), status_icon=status_icon) # Device is trusted or was already allowed, larger file -> display a notification, but auto-accept elif size > 350000: self._notification = Notification(_("Receiving file"), _("Receiving file %(0)s from %(1)s") % {"0": "<b>" + filename + "</b>", "1": "<b>" + name + "</b>"}, pixbuf=get_icon("blueman", 48), status_icon=status_icon) self._on_action(self._notification, 'accept') # Device is trusted or was already allowed. very small file -> auto-accept and transfer silently else: self._notification = None self._on_action(self._notification, "accept") def _on_cancel(self, agent): self._notification.close() agent.reply(obex.Error.Canceled)