Skip to content

Commit

Permalink
Determine KC's DBUS_SESSION_BUS_ADDRESS on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
qwerty12 committed Oct 30, 2021
1 parent 76c48d3 commit 5526cc2
Show file tree
Hide file tree
Showing 5 changed files with 393 additions and 3 deletions.
6 changes: 3 additions & 3 deletions dbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"strings"
"sync"

"github.com/godbus/dbus"
"github.com/godbus/dbus/v5"
)

const (
Expand Down Expand Up @@ -329,7 +329,7 @@ func logBadProp(id, prop string, err error) {
}

func newDevice(id string) (*Device, error) {
conn, err := dbus.SessionBus()
conn, err := DBusSessionBusForPlatform()
obj := conn.Object(dest, dbus.ObjectPath(fmt.Sprintf("%s/devices/%s", path, id)))
if err != nil {
return nil, err
Expand All @@ -352,7 +352,7 @@ func newDevice(id string) (*Device, error) {
}

func newDeviceList() (*deviceList, error) {
conn, err := dbus.SessionBus()
conn, err := DBusSessionBusForPlatform()
if err != nil {
return nil, err
}
Expand Down
10 changes: 10 additions & 0 deletions dbus_connection_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//go:build !windows
// +build !windows

package main

import "github.com/godbus/dbus/v5"

func DBusSessionBusForPlatform() (conn *dbus.Conn, err error) {
return dbus.SessionBus()
}
149 changes: 149 additions & 0 deletions dbus_connection_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//go:build windows
// +build windows

package main

import (
"bytes"
"crypto/sha1"
"fmt"
"io"
"os"
"reflect"
"strings"
"syscall"
"time"
"errors"
"unsafe"

"github.com/godbus/dbus/v5"
"golang.org/x/sys/windows"
)

var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procOpenFileMappingW = modkernel32.NewProc("OpenFileMappingW") //syscall only provides CreateFileMapping

cachedDbusSectionName = ""
)

func normaliseAndHashDbusPath(kdeConnectPath string) string {
if !strings.HasSuffix(kdeConnectPath, "\\") {
kdeConnectPath = kdeConnectPath + "\\"
}

if strings.HasSuffix(kdeConnectPath, "\\bin\\") {
kdeConnectPath = strings.TrimSuffix(kdeConnectPath, "bin\\")
}

kdeConnectPath = strings.ToLower(kdeConnectPath)

sha1 := sha1.New()
io.WriteString(sha1, kdeConnectPath)
kdeConnectPath = fmt.Sprintf("%x", sha1.Sum(nil)) // produces a lower-case hash, which is required

return kdeConnectPath
}

func readStringFromSection(sectionName string) (ret string, err error) {
lpSectionName, err := syscall.UTF16PtrFromString(sectionName)
if err != nil {
return "", err
}

var hSharedMem uintptr
for i := 0; i < 3; i++ {
hSharedMem, _, err = syscall.Syscall(procOpenFileMappingW.Addr(), 3, windows.FILE_MAP_READ, 0, uintptr(unsafe.Pointer(lpSectionName)))
if hSharedMem == 0 {
time.Sleep(100 * time.Millisecond)
} else {
break
}
}
if hSharedMem == 0 {
return "", err
}
defer windows.CloseHandle(windows.Handle(hSharedMem))

lpsharedAddr, err := syscall.MapViewOfFile(syscall.Handle(hSharedMem), windows.FILE_MAP_READ, 0, 0, 0)
if err != nil {
return "", err
}
defer syscall.UnmapViewOfFile(lpsharedAddr)

// obtain section size - rounded up to the page size
mbi := windows.MemoryBasicInformation{}
err = windows.VirtualQueryEx(windows.CurrentProcess(), uintptr(lpsharedAddr), &mbi, unsafe.Sizeof(mbi))
if err != nil {
return "", err
}

// get a byte[] representation of the mapping, using the same technique as syscall_unix.go
// alternatively, unsafe.Slice((*byte)(unsafe.Pointer(sec)), mbi.RegionSize) works, with a "possible misuse of unsafe.Pointer" warning
var bSharedAddr []byte
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&bSharedAddr))
hdr.Data = lpsharedAddr
hdr.Cap = int(mbi.RegionSize)
hdr.Len = int(mbi.RegionSize)

// copy section's contents into this process
dbusAddress := make([]byte, len(bSharedAddr))
copy(dbusAddress, bSharedAddr)
dbusAddress[len(dbusAddress) - 1] = 0 // force null-termination somewhere

// assuming a valid string, get the first null-terminator and cap the new slice's length to it
strlen := bytes.IndexByte(dbusAddress, 0)
return string(dbusAddress[:strlen]), nil
}

func DBusSessionBusForPlatform() (conn *dbus.Conn, err error) {
// If $DBUS_SESSION_BUS_ADDRESS is set, just use that
if this_env_sess_addr := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); this_env_sess_addr != "" {
return dbus.SessionBus()
}

var sectionName string = cachedDbusSectionName
if sectionName == "" {
// Prioritise Store-installed copies because it's more determinable
kdeConnectPath, err := KdeConnectPathFromWindowsStore()
if err != nil {
//fmt.Fprintln(os.Stderr, err)
kdeConnectPath, err = KdeConnectPathFromRunningIndicator()
}

if err != nil {
return nil, err
}

kdeConnectPath = normaliseAndHashDbusPath(kdeConnectPath)

sectionName = "Local\\DBusDaemonAddressInfo-" + kdeConnectPath
}

if sectionName != "" {
dbusAddress, err := readStringFromSection(sectionName)
if err != nil {
//cachedDbusSectionName = ""
return nil, err
}

conn, err := dbus.Connect(dbusAddress)
if err == nil {
// Keep a copy of the computed section name to avoid the path lookups,
// but not the section contents itself, as KDE Connect could be started again
// from the same path but using a different port.
// The odds of its install directory being changed on the other hand are low
cachedDbusSectionName = sectionName
} else {
cachedDbusSectionName = ""
}

return conn, err
}

if err == nil {
err = errors.New("could not determine KDE Connect's D-Bus daemon location")
}

return nil, err
}
79 changes: 79 additions & 0 deletions kdeconnect_path_running_process_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//go:build windows
// +build windows

package main

import (
"errors"
"unsafe"
"path/filepath"

"golang.org/x/sys/windows"
)

const (
WTS_CURRENT_SERVER_HANDLE = 0
WTS_CURRENT_SESSION = 0xFFFFFFFF
WTSTypeProcessInfoLevel0 = 0
)

type WTS_PROCESS_INFO struct {
SessionId uint32
ProcessId uint32
pProcessName *uint16
pUserSid uintptr // incomplete: avoid defining a struct for something unneeded
}

var (
libwtsapi32 = windows.NewLazySystemDLL("wtsapi32.dll")
procWTSEnumerateProcessesExW = libwtsapi32.NewProc("WTSEnumerateProcessesExW")
procWTSFreeMemoryExW = libwtsapi32.NewProc("WTSFreeMemoryExW")
)

func wtsFreeMemoryExW(WTSTypeClass uint32, pMemory unsafe.Pointer, NumberOfEntries uint32) {
procWTSFreeMemoryExW.Call(uintptr(WTSTypeClass), uintptr(pMemory), uintptr(NumberOfEntries))
}

func wtsEnumerateProcessesExW(hServer windows.Handle, pLevel *uint32, SessionId uint32, ppProcessInfo unsafe.Pointer, pCount *uint32) (ret bool, lastErr error) {
r1, _, lastErr := procWTSEnumerateProcessesExW.Call(uintptr(hServer), uintptr(unsafe.Pointer(pLevel)), uintptr(SessionId), uintptr(unsafe.Pointer(ppProcessInfo)), uintptr(unsafe.Pointer(pCount)))
return r1 != 0, lastErr
}

func KdeConnectPathFromRunningIndicator() (ret string, err error) {
var (
Level uint32 = WTSTypeProcessInfoLevel0
pProcessInfo *WTS_PROCESS_INFO
count uint32

hostSessionId uint32 = WTS_CURRENT_SESSION
)
// Only look at processes started in the same Windows session as the host is running in
windows.ProcessIdToSessionId(windows.GetCurrentProcessId(), &hostSessionId)

r1, lastErr := wtsEnumerateProcessesExW(WTS_CURRENT_SERVER_HANDLE, &Level, hostSessionId, unsafe.Pointer(&pProcessInfo), &count)
if !r1 {
return "", lastErr
}
defer wtsFreeMemoryExW(Level, unsafe.Pointer(pProcessInfo), count)

size := unsafe.Sizeof(WTS_PROCESS_INFO{})
for i := uint32(0); i < count; i++ {
p := *(*WTS_PROCESS_INFO)(unsafe.Pointer(uintptr(unsafe.Pointer(pProcessInfo)) + (uintptr(size) * uintptr(i))))
procName := windows.UTF16PtrToString(p.pProcessName)
if procName == "kdeconnect-indicator.exe" || procName == "kdeconnectd.exe" || procName == "kdeconnect-app.exe" {
hProcess, _ := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, p.ProcessId)
if hProcess != 0 {
var exeNameBuf [261]uint16 // MAX_PATH + 1
exeNameLen := uint32(len(exeNameBuf) - 1)
err = windows.QueryFullProcessImageName(hProcess, 0, &exeNameBuf[0], &exeNameLen)
windows.CloseHandle(hProcess)
if err == nil {
exeName := windows.UTF16ToString(exeNameBuf[:exeNameLen])
return filepath.Dir(exeName), nil
}
}
}
}

return "", errors.New("could not find KDE Connect processes")
}
Loading

0 comments on commit 5526cc2

Please sign in to comment.