Open qwerty12 opened 3 years ago
Well, this is quite a dance to go through. I do note that kdeconnect-cli.exe
works on windows, and I'd be somewhat surprised to discover they were using a similar method. Perhaps it might be possible to work out how they're discovering the bus address?
Also, I vaguely recall it being a requirement to write registry keys to install a native host on windows, do you know off-hand if my recollection is correct, and would that mean we'd need to deal with UAC or something for the installer?
I'm afraid I haven't spent any real time on Windows in decades, so I appreciate the assistance.
Oh, wow, after just now hearing of kdeconnect-cli
and trying it myself, I do not disagree. At least this isn't the code where it reads processes' environment variables... But I think have the gist (with assumptions made) of how it works:
KDE Connect uses QtDBus, naturally. QtDBus uses the original libdbus so it gets handling of any platform-specific implementation "quirks" for free
KDE Connect's D-Bus session.conf is configured with <listen>autolaunch:scope=*install-path</listen>
, so the path of where libdbus-1.dll is stored is key - its dirname is used to generate a hash
When dbus-daemon.exe starts and reads that specific <listen>
value, it creates a Windows Section with the name of DBusDaemonAddressInfo-$HASH
containing a valid DBUS_SESSION_BUS_ADDRESS for clients
For client programs using the same libdbus as the dbus-daemon.exe, like kdeconnect-cli
, libdbus reads the same listen
setting and comes up with the same Section name to open and read
Reading the Section and obtaining its contents is trivial. Finding KDE Connect's installation directory is not. There's two issues there:
The KDE Connect download page suggests getting it from the Microsoft Store. It installs it to C:\Program Files\WindowsApps\KDEe.V.KDEConnect_0.0.878.0_x64__7vt06qxq7ptv8\
- not exactly determinable. EDIT: I have Go code to get this path, so this isn't a problem any more.
But that's not the only way to install KDE Connect. An NSIS-based installer is offered. It offers standard system-wide and per-user paths, but also the choice to install it to an arbitrarily-chosen directory. For the standard paths, they could probably be hardcoded, but otherwise I do not currently know if the NSIS installer stores the installation path in the registry and if so, can it be easily found (no use of random GUIDs etc.). In the case of non-Microsoft Store installs, it might be easier to just get the kdeconnect-chrome-extension installer to ask the user for the path of KDE Connect if it's not found in the usual places and store it in the registry or as an environment variable.
EDIT: Alternatively, It would actually be easier to just take the path of the running kdeconnect-indicator.exe...
Once KDE Connect's path has been determined, the hashing can be done in kdeconnect-chrome-extension. I looked at the D-Bus code: you take the location of where libdbus.dll is, dirname()
it, strip "\bin" off the end if present, and then make the path (with trailing backslash) lower-case and then just SHA1 the path (and then lower-case the result)
Also, I vaguely recall it being a requirement to write registry keys to install a native host on windows, do you know off-hand if my recollection is correct, and would that mean we'd need to deal with UAC or something for the installer?
You're right, but UAC would only be an issue if you're wanting to do a system-wide install (really, you'd just have to relaunch). Both Chrome (and its derivatives I presume) and Firefox read a per-user registry key on Windows for manifest paths, which doesn't require special privileges to write to.
I'm afraid I haven't spent any real time on Windows in decades, so I appreciate the assistance.
I'm very much appreciative of this. The approach I've used currently may be... well... convoluted but kdeconnect-chrome-extension has saved me time nevertheless on Windows.
This is good analysis, not as straight-forward as I was hoping. I'm open to this approach if you agree it would be cleaner than the current method.
I'll sort out the installer stuff once it's decided what the path is here.
I'm not sure on what GitHub PR etiquette is, to be honest, so I've just rebased the branch this PR is pointing to with code that does the Section reading. It is more complex, but the possible race condition between reading the TCP tables and scanning the processes isn't a problem any more. EDIT: unless you're getting something else calling itself kdeconnect-indicator.exe, this way will always connect to KDE Connect's D-Bus daemon too
To try and obtain KDE Connect's path, the following is tried:
Using the package manager API to determine if there's a copy of KDE Connect installed from the Windows Store and if so, use its path
For any running kdeconnect-indicator.exe/kdeconnectd.exe/kdeconnect-indicator.exe process, take the path of the first match. Should work for installs from elsewhere (or if the Store code fails)
For the Windows Store code, Windows.Management.Deployment
is a COM API. The code to utilise it is ugly (but I'm 99.999999% sure I free everything that needs to be freed) and relies on the https://github.com/go-ole/go-ole library.
Maybe it's the way I've written it, but the COM code does look really ugly in Go. It's pretty much the equivalent of this, which is much easier to read.
I've split the path determination code for both ways into separate files for readability purposes. I can merge the code into one file or get rid of a method entirely. Both do not rely on any undocumented code; however, borrowing a trick from syscall_unix.go's Mmap
, the code to read from the section does use reflect.SliceHeader
to turn the mapped view's uintptr
result into a []byte
.
Once the path has been found, the hashed version is cached (naïvely - assumes a single thread) to avoid the path lookups. But the Section is always read. IMO the path isn't likely to change, but relaunching KDE Connect (slightly more likely to happen) may cause its dbus-daemon to change ports.
I do not know Go at all, so if there's basic errors or better ways to do things (or even better names for functions or variables), let me know or just have at it. Cheers.
Sorry for the delay, been a little busy. I think my preference would be to include only the generic implementation that searches for running processes, since that gives us the best coverage for install methods, and lets us drop a bunch of code. I'll try to find time for a proper review over this weekend.
No worries. Getting rid of the Windows Store-specific code is probably for the best... The one thing I need to add: capping the maximum amount of data read from the Section/Mapping, as I cannot imagine in practice a DBUS_SESSION_BUS_ADDRESS greater than, say, 64Kb. Thanks.
- It installs it to
C:\Program Files\WindowsApps\KDEe.V.KDEConnect_0.0.878.0_x64__7vt06qxq7ptv8\
- not exactly determinable.
You should not use that path. And you can't access it easily anyway. Just use its execution alias, which has a stable path ("C:\Users\yourprofilename\AppData\Local\Microsoft\WindowsApps\kdeconnect-cli.exe" or just determine it from PATH variable, since it has this path added). This will make things much easier.
An execution alias wouldn't help any here. libdbus, when creating its Section with the information that is needed, doesn't use one but does hash the actual path it's in. In any case, getting the actual path isn't a problem any more - it's pulled from the running processes. I did write code to read it from the Windows APIs designed for getting Store applications' information, but removed it following pdf's comments.
libdbus, when creating its Section with the information that is needed, doesn't use one but does hash the actual path it's in
Oh, in that case you are doing it right. Sorry. But the path changes every time the app is updated. Is that going to be a problem?
But the path changes every time the app is updated. Is that going to be a problem?
Yes. This code caches the Section name to open, which, as you point out, is liable to change in the case of an update. I don't consider fixing it a huge priority yet, because there's no way (as far as I can tell) to get this native extension to refresh its connection to KDE Connect's D-Bus daemon anyway*. In the two months I've been running this on Windows, I've only had it mess up once, at which point I just disabled and re-enabled the extension and all was well...
* I assume there's no need for such a thing in Linux, where the Session Bus is provided by the system
I've only had it mess up once, at which point I just disabled and re-enabled the extension and all was well...
So basically, it's refreshed every time you restart a browser?
Yes
I feel like I need to apologise for the further delays here - I have some of the required changes staged, but I need to replace the installer code with something that will work on Windows and I just haven't made the time yet.
This adds support for determining KDE Connect's dbus-daemon's session bus's address on Windows. I haven't tested to see if this breaks building on Linux.
Outdated
It works like this: 1. Get a list of processes in the same [Windows session](https://brianbondy.com/blog/100/understanding-windows-at-a-deeper-level-sessions-window-stations-and-desktops) as the one kdeconnect-chrome-extension.exe is running in (on a system where multiple users are logged in at the same time, this avoids possibly connecting to the wrong KDE Connect instance) 2. For any dbus-daemon.exe processes, store their PIDs 3. Read the system's TCP table and for anything listening to a port bound on 127.0.0.1, see if the PID matches one collected in step 2 4. If so, call `org.freedesktop.DBus.NameHasOwner` and see if `org.kde.kdeconnect` has registered on the bus. If that is the case, the connection string is cached and the connection is returnedSee https://github.com/pdf/kdeconnect-chrome-extension/pull/44#issuecomment-955607153
If this is acceptable, the remaning two issues for fully fledged Windows support would be to adapt the installer (climenu won't build on Windows) and updating godbus to v5.
(As an aside, if someone's interested in adding macOS support...)
EDIT: This requires a newer version of golang.org/x/sys than the one that is automatically pulled in by
glide
as a dependency