altdesktop / python-dbus-next

🚌 The next great DBus library for Python with asyncio support
https://python-dbus-next.readthedocs.io/en/latest/
MIT License
199 stars 59 forks source link

Does dbus-next work in Windows? #157

Open pozzugno opened 1 year ago

pozzugno commented 1 year ago

I have installed Python 3.8 on my Windows 10 computer. I installed successfully dbus-next with pip, but I wasn't able to run a working application.

After launching dbus-daemon.exe and setting a suitable bus address for windows ("tcp:host=localhost,port=124342"), now I receive this error:

File "C:\Users\.....py", line 37, in main
    bus = await MessageBus(bus_type=BusType.SYSTEM, bus_address=BUS_ADDRESS).connect()
  File "C:\python38\lib\site-packages\dbus_next\aio\message_bus.py", line 149, in connect
    await self._authenticate()
  File "C:\python38\lib\site-packages\dbus_next\aio\message_bus.py", line 382, in _authenticate
    first_line = self._auth._authentication_start(negotiate_unix_fd=self._negotiate_unix_fd)
  File "C:\python38\lib\site-packages\dbus_next\auth.py", line 62, in _authentication_start
    hex_uid = str(os.getuid()).encode().hex()
AttributeError: module 'os' has no attribute 'getuid'

I suspect dbus-next is not compatible with Windows.

Kochise commented 12 months ago

I suspect dbus-next is not compatible with Windows.

https://docs.python.org/3/library/os.html#os.getuid : Availability: Unix.

Hence NO, indeed.

ErikHons commented 8 months ago

I have dbus-next working on Windows. I needed to override the default asyncio loop policy, create an external authenticator, and then ensure that the first address in DBUS_SESSION_BUS_ADDRESS was a tcp loopback. That's not nothing but it works fine once set up.

The policy is set thusly:

    if platform.system() == "Windows":
        # dbus-next uses loop.add_read_reader() and loop_add_writer() to
        # watch file descriptors. Those methods aren't supported on the
        # default event-loop implementation on Windows so set the event
        # loop policy to always use the SelectorEventLoop.
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

This works as an authenticator:

import win32api
import win32security
from dbus_next.auth import Authenticator, _AuthResponse
from dbus_next.errors import AuthError
from ntsecuritycon import TOKEN_QUERY, TokenUser

class AuthWindowsExternal(Authenticator):
    """An authenticator class for the Windows SID-based external auth protocol for use with the
    :class:`MessageBus <dbus_next.message_bus.BaseMessageBus>`.

    :sealso: https://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
    """

    def __init__(self):
        pass

    def _authentication_start(self, negotiate_unix_fd=False) -> str:
        assert negotiate_unix_fd == False

        tok = win32security.OpenProcessToken(win32api.GetCurrentProcess(), TOKEN_QUERY)
        sid, _ = win32security.GetTokenInformation(tok, TokenUser)
        win32api.CloseHandle(tok)
        hex_uid = str(sid).removeprefix("PySID:").encode().hex()
        return f"AUTH EXTERNAL {hex_uid}"

    def _receive_line(self, line: str):
        response, args = _AuthResponse.parse(line)

        if response is _AuthResponse.OK:
            return "BEGIN"

        raise AuthError(f"authentication failed: {response.value}: {args}")

Which you pass into dbus-next thusly: dbus_next.aio.message_bus.MessageBus(auth=windows.AuthWindowsExternal())

ErikHons commented 8 months ago

I suspect dbus-next is not compatible with Windows.

https://docs.python.org/3/library/os.html#os.getuid : Availability: Unix.

Hence NO, indeed.

Fortunately that method is only accessed from the AuthExternal class which you have to replace for Windows anyway.