From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/Makefile.in | 1 + dlls/bluetoothapis/bluetoothapis.rc | 33 ++++++ dlls/bluetoothapis/main.c | 163 +++++++++++++++++++++++++++- dlls/bluetoothapis/resource.h | 27 +++++ 4 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 dlls/bluetoothapis/bluetoothapis.rc create mode 100644 dlls/bluetoothapis/resource.h
diff --git a/dlls/bluetoothapis/Makefile.in b/dlls/bluetoothapis/Makefile.in index ccdf17d2609..d4568065953 100644 --- a/dlls/bluetoothapis/Makefile.in +++ b/dlls/bluetoothapis/Makefile.in @@ -5,5 +5,6 @@ IMPORTS = setupapi cfgmgr32 user32 EXTRADLLFLAGS = -Wb,--prefer-native
SOURCES = \ + bluetoothapis.rc \ main.c \ sdp.c diff --git a/dlls/bluetoothapis/bluetoothapis.rc b/dlls/bluetoothapis/bluetoothapis.rc new file mode 100644 index 00000000000..0d2469de075 --- /dev/null +++ b/dlls/bluetoothapis/bluetoothapis.rc @@ -0,0 +1,33 @@ +/* + * Bluetooth API resources + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "resource.h" + +#pragma makedep po + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +{ + IDS_AUTH_WIZARD_PAIR_REQUEST_TITLE, "Confirm pairing" + IDS_AUTH_WIZARD_NAMED_PAIR_REQUEST, "Pair with '%s' using the following PIN/passkey value: %lu?" + IDS_AUTH_WIZARD_UNNAMED_PAIR_REQUEST, "Pair with unnamed device %02X:%02X:%02X:%02X:%02X:%02X using the following PIN/passkey value: %lu?" +} diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index ffe0a4a5870..1c2d69b7c0d 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -42,8 +42,12 @@ #include "wine/debug.h" #include "wine/list.h"
+#include "resource.h" + WINE_DEFAULT_DEBUG_CHANNEL(bluetoothapis);
+static HINSTANCE instance; + struct bluetooth_find_radio_handle { HDEVINFO devinfo; @@ -670,6 +674,14 @@ struct bluetooth_auth_listener void *user_data; };
+struct bluetooth_auth_wizard_listener +{ + struct list entry; + ULONG refcount; + + BLUETOOTH_ADDRESS addr; +}; + static const char *debugstr_bluetooth_auth_listener( const struct bluetooth_auth_listener *listener ) { if (!listener) @@ -681,7 +693,13 @@ static const char *debugstr_bluetooth_auth_listener( const struct bluetooth_auth }
static SRWLOCK bluetooth_auth_lock = SRWLOCK_INIT; -static struct list bluetooth_auth_listeners = LIST_INIT( bluetooth_auth_listeners ); /* Guarded by bluetooth_auth_lock */ +/* A list of bluetooth_auth_listener objects added by BluetoothRegisterForAuthenticationEx. + * Guarded by bluetooth_auth_lock. */ +static struct list bluetooth_auth_listeners = LIST_INIT( bluetooth_auth_listeners ); +/* A list of fallback "listeners" created by BluetoothAuthenticateDeviceEx, used if no matching listener object + * could be found in bluetooth_auth_listeners. We use this to decide which pairing requests can be forwarded to a user + * wizard. Guarded by bluetooth_auth_lock. */ +static struct list bluetooth_auth_wizard_listeners = LIST_INIT( bluetooth_auth_wizard_listeners ); static HCMNOTIFICATION bluetooth_auth_event_notify; /* Guarded by bluetooth_auth_lock */
struct auth_callback_data @@ -708,11 +726,88 @@ static CALLBACK void tp_auth_callback_proc( TP_CALLBACK_INSTANCE *instance, void free( data ); }
+static void bluetooth_auth_wizard_ask_response( struct bluetooth_auth_wizard_listener *listener, + const struct winebth_authentication_request *request ) +{ + BLUETOOTH_FIND_RADIO_PARAMS find_params = {.dwSize = sizeof( find_params )}; + struct winebth_radio_send_auth_response_params params = {0}; + HBLUETOOTH_RADIO_FIND radio_find; + BLUETOOTH_ADDRESS addr; + DWORD ret, bytes; + char format[1024], msg[1024], title[1024]; + HANDLE radio; + + switch (request->auth_method) + { + case BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON: + case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION: + break; + default: + FIXME( "Unsupported authentication method: %d\n", request->auth_method ); + goto done; + } + + addr.ullLong = request->device_info.address; + + if (request->device_info.flags & BDIF_NAME) + { + LoadStringA( instance, IDS_AUTH_WIZARD_NAMED_PAIR_REQUEST, format, sizeof( format ) ); + snprintf( msg, sizeof( msg ), format, request->device_info.name, request->numeric_value_or_passkey ); + } + else + { + LoadStringA( instance, IDS_AUTH_WIZARD_UNNAMED_PAIR_REQUEST, format, sizeof( format ) ); + snprintf( msg, sizeof( msg ), format, addr.rgBytes[0], addr.rgBytes[1], addr.rgBytes[2], addr.rgBytes[3], + addr.rgBytes[4], addr.rgBytes[5], request->numeric_value_or_passkey ); + } + LoadStringA( instance, IDS_AUTH_WIZARD_PAIR_REQUEST_TITLE, title, sizeof( title ) ); + ret = MessageBoxA( NULL, msg, title, MB_YESNO ); + if (!ret) + { + ERR("MessageBoxW failed: %lu\n", ret ); + goto done; + } + + params.address = RtlUlonglongByteSwap( addr.ullLong ) >> 16; + params.method = request->auth_method; + params.numeric_value_or_passkey = request->numeric_value_or_passkey; + params.negative = ret != IDYES; + + radio_find = BluetoothFindFirstRadio( &find_params, &radio ); + if (!radio_find) + { + ERR( "BluetoothFindFirstRadio failed: %lu\n", GetLastError() ); + goto done; + } + + do { + if (DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SEND_AUTH_RESPONSE, ¶ms, sizeof( params ), + ¶ms, sizeof( params ), &bytes, NULL )) + { + ret = ERROR_SUCCESS; + goto done; + } + ret = GetLastError(); + CloseHandle( radio ); + } while (BluetoothFindNextRadio( radio_find, &radio )); + + BluetoothFindRadioClose( radio_find ); + if (ret) + ERR( "Failed to send pairing response: %lu\n", ret ); +done: + if (!--listener->refcount) + { + list_remove( &listener->entry ); + free( listener ); + } +} + static CALLBACK DWORD bluetooth_auth_event_callback( HCMNOTIFICATION notify, void *ctx, CM_NOTIFY_ACTION action, CM_NOTIFY_EVENT_DATA *event_data, DWORD size ) { struct winebth_authentication_request *request = (struct winebth_authentication_request *)event_data->u.DeviceHandle.Data; struct bluetooth_auth_listener *listener; + BOOL try_wizard = TRUE;
TRACE( "(%p, %p, %d, %p, %lu)\n", notify, ctx, action, event_data, size );
@@ -725,13 +820,14 @@ static CALLBACK DWORD bluetooth_auth_event_callback( HCMNOTIFICATION notify, voi break; }
- AcquireSRWLockShared( &bluetooth_auth_lock ); + AcquireSRWLockExclusive( &bluetooth_auth_lock ); LIST_FOR_EACH_ENTRY( listener, &bluetooth_auth_listeners, struct bluetooth_auth_listener, entry ) { struct auth_callback_data *data;
if (!(listener->all_devices || listener->addr.ullLong == request->device_info.address)) continue; + try_wizard = FALSE; data = calloc( 1, sizeof( *data ) ); if (!data) continue; @@ -746,7 +842,22 @@ static CALLBACK DWORD bluetooth_auth_event_callback( HCMNOTIFICATION notify, voi continue; } } - ReleaseSRWLockShared( &bluetooth_auth_lock ); + + /* No appropriate registered callback could be found for this request. Lets ask the user what to do instead. */ + if (try_wizard) + { + struct bluetooth_auth_wizard_listener *listener, *next; + LIST_FOR_EACH_ENTRY_SAFE( listener, next, &bluetooth_auth_wizard_listeners, + struct bluetooth_auth_wizard_listener, entry ) + { + if (listener->addr.ullLong == request->device_info.address) + { + bluetooth_auth_wizard_ask_response( listener, request ); + break; + } + } + } + ReleaseSRWLockExclusive( &bluetooth_auth_lock ); break; default: FIXME( "Unexpected CM_NOTIFY_ACTION: %d\n", action ); @@ -953,6 +1064,8 @@ DWORD WINAPI BluetoothAuthenticateDeviceEx( HWND parent, HANDLE handle_radio, BL AUTHENTICATION_REQUIREMENTS auth_req ) { OVERLAPPED ovl = {0}; + struct bluetooth_auth_wizard_listener *listener; + BOOL wizard_listener_exists; BTH_ADDR device_addr; BOOL success; DWORD ret, bytes; @@ -986,6 +1099,40 @@ DWORD WINAPI BluetoothAuthenticateDeviceEx( HWND parent, HANDLE handle_radio, BL }
ovl.hEvent = CreateEventW( NULL, TRUE, FALSE, NULL ); + AcquireSRWLockExclusive( &bluetooth_auth_lock ); + if (list_empty( &bluetooth_auth_listeners ) && list_empty( &bluetooth_auth_wizard_listeners )) + { + ret = bluetooth_auth_register(); + if (ret) + { + ReleaseSRWLockExclusive( &bluetooth_auth_lock ); + return ret; + } + } + + wizard_listener_exists = FALSE; + LIST_FOR_EACH_ENTRY( listener, &bluetooth_auth_wizard_listeners, struct bluetooth_auth_wizard_listener, entry ) + { + if (listener->addr.ullLong == device_info->Address.ullLong) + { + listener->refcount++; + wizard_listener_exists = TRUE; + break; + } + } + if (!wizard_listener_exists) + { + listener = calloc( 1, sizeof( *listener ) ); + if (!listener) + { + ReleaseSRWLockExclusive( &bluetooth_auth_lock ); + return ERROR_OUTOFMEMORY; + } + listener->addr = device_info->Address; + list_add_tail( &bluetooth_auth_wizard_listeners, &listener->entry ); + } + ReleaseSRWLockExclusive( &bluetooth_auth_lock ); + device_addr = RtlUlonglongByteSwap( device_info->Address.ullLong ) >> 16; success = DeviceIoControl( handle_radio, IOCTL_WINEBTH_RADIO_START_AUTH, &device_addr, sizeof( device_addr ), NULL, 0, &bytes, &ovl ); @@ -1032,3 +1179,13 @@ DWORD WINAPI BluetoothRemoveDevice( BLUETOOTH_ADDRESS *addr ) BluetoothFindRadioClose( radio_find ); return success ? ERROR_SUCCESS : ERROR_NOT_FOUND; } + +BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved ) +{ + TRACE( "(%p, %lu, %p)\n", inst, reason, reserved ); + + if (reason == DLL_PROCESS_ATTACH) + instance = inst; + + return TRUE; +} diff --git a/dlls/bluetoothapis/resource.h b/dlls/bluetoothapis/resource.h new file mode 100644 index 00000000000..6c151fee922 --- /dev/null +++ b/dlls/bluetoothapis/resource.h @@ -0,0 +1,27 @@ +/* + * Bluetooth API resources + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include <windef.h> +#include <winuser.h> + +#define IDS_AUTH_WIZARD_PAIR_REQUEST_TITLE 1 +#define IDS_AUTH_WIZARD_NAMED_PAIR_REQUEST 2 +#define IDS_AUTH_WIZARD_UNNAMED_PAIR_REQUEST 3