Add `IOCTL_WINEBTH_RADIO_START_AUTH`, which gets used to implement `BluetoothAuthenticateDeviceEx`.
-- v8: bluetoothapis/tests: Add tests for BluetoothAuthenticateDeviceEx. bluetoothapis: Use a wizard GUI to respond to pairing requests if an authentication callback is not registered. bluetoothapis: Implement BluetoothAuthenticateDeviceEx.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 76 ++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 9 ++++ dlls/winebth.sys/unixlib.h | 7 +++ dlls/winebth.sys/unixlib_priv.h | 1 + dlls/winebth.sys/winebluetooth.c | 11 ++++ dlls/winebth.sys/winebth.c | 86 +++++++++++++++++++++++++++++++- dlls/winebth.sys/winebth_priv.h | 12 ++++- include/wine/winebth.h | 7 +++ 8 files changed, 205 insertions(+), 4 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 9a4aa89bbee..36f5a8cc467 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -1266,6 +1266,76 @@ NTSTATUS bluez_device_disconnect( void *connection, const char *device_path ) return STATUS_SUCCESS; }
+static BOOL bluez_event_list_queue_new_event( struct list *event_list, + enum winebluetooth_watcher_event_type event_type, + union winebluetooth_watcher_event_data event ); +struct bluez_device_pair_data +{ + IRP *irp; + struct bluez_watcher_ctx *watcher_ctx; +}; + +static void bluez_device_pair_callback( DBusPendingCall *pending, void *param ) +{ + struct bluez_device_pair_data *data = param; + DBusMessage *reply; + DBusError error; + union winebluetooth_watcher_event_data event = {0}; + + event.pairing_finished.irp = data->irp; + reply = p_dbus_pending_call_steal_reply( pending ); + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message( &error, reply )) + { + event.pairing_finished.result = bluez_dbus_error_to_ntstatus( &error ); + ERR( "Failed to pair: %s: %s\n", debugstr_a( error.name ), debugstr_a( error.message ) ); + } + p_dbus_error_free( &error ); + + bluez_event_list_queue_new_event( &data->watcher_ctx->event_list, + BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED, event ); + p_dbus_message_unref( reply ); + p_dbus_pending_call_unref( pending ); +} + +NTSTATUS bluez_device_start_pairing( void *connection, void *watcher_ctx, struct unix_name *device, IRP *irp ) +{ + DBusMessage *request; + DBusPendingCall *pending_call = NULL; + struct bluez_device_pair_data *data; + dbus_bool_t success; + + TRACE( "(%p, %p, %s, %p)\n", connection, watcher_ctx, debugstr_a( device->str ), irp ); + + request = p_dbus_message_new_method_call( BLUEZ_DEST, device->str, BLUEZ_INTERFACE_DEVICE, "Pair" ); + if (!request) + return STATUS_NO_MEMORY; + + data = malloc( sizeof( *data ) ); + if (!data) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + data->irp = irp; + data->watcher_ctx = watcher_ctx; + success = p_dbus_connection_send_with_reply( connection, request, &pending_call, bluez_timeout ); + p_dbus_message_unref( request ); + if (!success) + return STATUS_NO_MEMORY; + if (!pending_call) + return STATUS_INTERNAL_ERROR; + if (!p_dbus_pending_call_set_notify( pending_call, bluez_device_pair_callback, data, free )) + { + p_dbus_pending_call_cancel( pending_call ); + p_dbus_pending_call_unref( pending_call ); + return STATUS_NO_MEMORY; + } + + p_dbus_pending_call_unref( pending_call ); + return STATUS_PENDING; +} + struct bluez_watcher_event { struct list entry; @@ -1825,6 +1895,8 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED: unix_name_free( (struct unix_name *)event1->event.device_props_changed.device.handle ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED: + break; } free( event1 ); } @@ -2195,5 +2267,9 @@ NTSTATUS bluez_device_disconnect( void *connection, const char *device_path ) { return STATUS_NOT_SUPPORTED; } +NTSTATUS bluez_device_start_pairing( void *connection, void *watcher_ctx, struct unix_name *device, IRP *irp ) +{ + return STATUS_NOT_SUPPORTED; +}
#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 249137dd502..543c6d2ff1a 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -240,6 +240,14 @@ static NTSTATUS bluetooth_device_disconnect( void *args ) return bluez_device_disconnect( dbus_connection, params->device->str ); }
+static NTSTATUS bluetooth_device_start_pairing( void *args ) +{ + struct bluetooth_device_start_pairing_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_device_start_pairing( dbus_connection, bluetooth_watcher, params->device, params->irp ); +} + static NTSTATUS bluetooth_get_event( void *args ) { struct bluetooth_get_event_params *params = args; @@ -262,6 +270,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = {
bluetooth_device_free, bluetooth_device_disconnect, + bluetooth_device_start_pairing,
bluetooth_auth_agent_enable_incoming, bluetooth_auth_send_response, diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 7357f777804..273023f5b16 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -100,6 +100,12 @@ struct bluetooth_auth_send_response_params BOOL *authenticated; };
+struct bluetooth_device_start_pairing_params +{ + unix_name_t device; + IRP *irp; +}; + struct bluetooth_get_event_params { struct winebluetooth_event result; @@ -119,6 +125,7 @@ enum bluetoothapis_funcs
unix_bluetooth_device_free, unix_bluetooth_device_disconnect, + unix_bluetooth_device_start_pairing,
unix_bluetooth_auth_agent_enable_incoming, unix_bluetooth_auth_send_response, diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index a9c1bc0f080..2ee0272f215 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -62,6 +62,7 @@ extern NTSTATUS bluez_auth_agent_send_response( void *auth_agent, struct unix_na BLUETOOTH_AUTHENTICATION_METHOD method, UINT32 numeric_or_passkey, BOOL negative, BOOL *authenticated ); extern NTSTATUS bluez_device_disconnect( void *connection, const char *device_path ); +extern NTSTATUS bluez_device_start_pairing( void *dbus_connection, void *watcher_ctx, struct unix_name *device, IRP *irp ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); extern void bluez_watcher_close( void *connection, void *ctx ); #endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index ca234b98f4b..448fd3f4dda 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -177,6 +177,17 @@ NTSTATUS winebluetooth_auth_send_response( winebluetooth_device_t device, BLUETO return UNIX_BLUETOOTH_CALL( bluetooth_auth_send_response, &args ); }
+NTSTATUS winebluetooth_device_start_pairing( winebluetooth_device_t device, IRP *irp ) +{ + struct bluetooth_device_start_pairing_params args = {0}; + + TRACE( "(%p)\n", (void *)device.handle ); + + args.device = device.handle; + args.irp = irp; + return UNIX_BLUETOOTH_CALL( bluetooth_device_start_pairing, &args ); +} + NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result ) { struct bluetooth_get_event_params params = {0}; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 694c9f1d629..47006d04aad 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -81,6 +81,9 @@ struct bluetooth_radio
CRITICAL_SECTION remote_devices_cs; struct list remote_devices; /* Guarded by remote_devices_cs */ + + /* Guarded by device_list_cs */ + LIST_ENTRY irp_list; };
struct bluetooth_remote_device @@ -329,6 +332,47 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) LeaveCriticalSection( &ext->remote_devices_cs ); break; } + case IOCTL_WINEBTH_RADIO_START_AUTH: + { + const struct winebth_radio_start_auth_params *params = irp->AssociatedIrp.SystemBuffer; + struct bluetooth_remote_device *device; + + if (!params) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof( *params )) + { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + status = STATUS_DEVICE_DOES_NOT_EXIST; + EnterCriticalSection( &device_list_cs ); + EnterCriticalSection( &ext->remote_devices_cs ); + LIST_FOR_EACH_ENTRY( device, &ext->remote_devices, struct bluetooth_remote_device, entry ) + { + BOOL matches; + EnterCriticalSection( &device->props_cs ); + matches = device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS && + device->props.address.ullLong == params->address; + LeaveCriticalSection( &device->props_cs ); + if (matches) + { + status = winebluetooth_device_start_pairing( device->device, irp ); + if (status == STATUS_PENDING) + { + IoMarkIrpPending( irp ); + InsertTailList( &ext->irp_list, &irp->Tail.Overlay.ListEntry ); + } + break; + } + } + LeaveCriticalSection( &ext->remote_devices_cs ); + LeaveCriticalSection( &device_list_cs ); + break; + } case IOCTL_WINEBTH_RADIO_REMOVE_DEVICE: { const BTH_ADDR *param = irp->AssociatedIrp.SystemBuffer; @@ -369,8 +413,11 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) break; }
- irp->IoStatus.Status = status; - IoCompleteRequest( irp, IO_NO_INCREMENT ); + if (status != STATUS_PENDING) + { + irp->IoStatus.Status = status; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + } return status; }
@@ -483,6 +530,7 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added
InitializeCriticalSection( &device->props_cs ); InitializeCriticalSection( &device->remote_devices_cs ); + InitializeListHead( &device->irp_list );
EnterCriticalSection( &device_list_cs ); list_add_tail( &device_list, &device->entry ); @@ -797,6 +845,16 @@ static void bluetooth_radio_report_auth_event( struct winebluetooth_auth_event e ExFreePool( notification ); }
+static void complete_irp( IRP *irp, NTSTATUS result ) +{ + EnterCriticalSection( &device_list_cs ); + RemoveEntryList( &irp->Tail.Overlay.ListEntry ); + LeaveCriticalSection( &device_list_cs ); + + irp->IoStatus.Status = result; + IoCompleteRequest( irp, IO_NO_INCREMENT ); +} + static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { NTSTATUS status; @@ -832,6 +890,10 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED: bluetooth_radio_update_device_props( event->event_data.device_props_changed); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED: + complete_irp( event->event_data.pairing_finished.irp, + event->event_data.pairing_finished.result ); + break; default: FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); } @@ -994,6 +1056,21 @@ static void bluetooth_radio_set_properties( DEVICE_OBJECT *obj, sizeof( props->version ), &props->version ); }
+/* Caller should hold device_list_cs. */ +static void remove_pending_irps( struct bluetooth_radio *radio ) +{ + LIST_ENTRY *entry; + IRP *irp; + + while ((entry = RemoveHeadList( &radio->irp_list )) != &radio->irp_list) + { + irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry ); + irp->IoStatus.Status = STATUS_DELETE_PENDING; + irp->IoStatus.Information = 0; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + } +} + static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); @@ -1031,6 +1108,10 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) break; case IRP_MN_REMOVE_DEVICE: assert( device->removed ); + EnterCriticalSection( &device_list_cs ); + remove_pending_irps( device ); + LeaveCriticalSection( &device_list_cs ); + if (device->bthport_symlink_name.Buffer) { IoSetDeviceInterfaceState(&device->bthport_symlink_name, FALSE); @@ -1048,6 +1129,7 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) break; case IRP_MN_SURPRISE_REMOVAL: EnterCriticalSection( &device_list_cs ); + remove_pending_irps( device ); if (!device->removed) { device->removed = TRUE; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 5456acdd7a3..5addf6303ec 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -214,7 +214,7 @@ NTSTATUS winebluetooth_device_disconnect( winebluetooth_device_t device );
NTSTATUS winebluetooth_auth_send_response( winebluetooth_device_t device, BLUETOOTH_AUTHENTICATION_METHOD method, UINT32 numeric_or_passkey, BOOL negative, BOOL *authenticated ); - +NTSTATUS winebluetooth_device_start_pairing( winebluetooth_device_t device, IRP *irp ); enum winebluetooth_watcher_event_type { BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, @@ -222,7 +222,8 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_REMOVED, - BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED + BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED, + BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED, };
struct winebluetooth_watcher_event_radio_added @@ -264,6 +265,12 @@ struct winebluetooth_watcher_event_device_removed winebluetooth_device_t device; };
+struct winebluetooth_watcher_event_pairing_finished +{ + IRP *irp; + NTSTATUS result; +}; + union winebluetooth_watcher_event_data { struct winebluetooth_watcher_event_radio_added radio_added; @@ -272,6 +279,7 @@ union winebluetooth_watcher_event_data struct winebluetooth_watcher_event_device_added device_added; struct winebluetooth_watcher_event_device_removed device_removed; struct winebluetooth_watcher_event_device_props_changed device_props_changed; + struct winebluetooth_watcher_event_pairing_finished pairing_finished; };
struct winebluetooth_watcher_event diff --git a/include/wine/winebth.h b/include/wine/winebth.h index d0fb06c11e0..c785d08d88e 100644 --- a/include/wine/winebth.h +++ b/include/wine/winebth.h @@ -32,6 +32,8 @@ /* Ask the system's Bluetooth service to send all incoming authentication requests to Wine. */ #define IOCTL_WINEBTH_AUTH_REGISTER CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa8, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_RADIO_SEND_AUTH_RESPONSE CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa9, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* Initiate the authentication procedure with a remote device. */ +#define IOCTL_WINEBTH_RADIO_START_AUTH CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xaa, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_RADIO_REMOVE_DEVICE CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xab, METHOD_BUFFERED, FILE_ANY_ACCESS)
DEFINE_GUID( GUID_WINEBTH_AUTHENTICATION_REQUEST, 0xca67235f, 0xf621, 0x4c27, 0x85, 0x65, 0xa4, @@ -68,6 +70,11 @@ struct winebth_radio_send_auth_response_params unsigned int authenticated : 1; };
+struct winebth_radio_start_auth_params +{ + BTH_ADDR address; +}; + #pragma pack(pop)
#endif /* __WINEBTH_H__ */
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/Makefile.in | 2 +- dlls/bluetoothapis/bluetoothapis.spec | 1 + dlls/bluetoothapis/main.c | 53 +++++++++++++++++++++++++++ dlls/bthprops.cpl/bthprops.cpl.spec | 2 +- include/bluetoothapis.h | 11 ++++++ 5 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/dlls/bluetoothapis/Makefile.in b/dlls/bluetoothapis/Makefile.in index 996c561fc17..ccdf17d2609 100644 --- a/dlls/bluetoothapis/Makefile.in +++ b/dlls/bluetoothapis/Makefile.in @@ -1,6 +1,6 @@ MODULE = bluetoothapis.dll IMPORTLIB = bluetoothapis -IMPORTS = setupapi cfgmgr32 +IMPORTS = setupapi cfgmgr32 user32
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/bluetoothapis/bluetoothapis.spec b/dlls/bluetoothapis/bluetoothapis.spec index 46b1903a7a7..e26932496dc 100644 --- a/dlls/bluetoothapis/bluetoothapis.spec +++ b/dlls/bluetoothapis/bluetoothapis.spec @@ -1,4 +1,5 @@ @ stub BluetoothAddressToString +@ stdcall BluetoothAuthenticateDeviceEx(ptr ptr ptr ptr long) @ stub BluetoothDisconnectDevice @ stdcall BluetoothEnableDiscovery(ptr long) @ stdcall BluetoothEnableIncomingConnections(ptr long) diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index d24a40fd1de..ffe0a4a5870 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -945,6 +945,59 @@ DWORD WINAPI BluetoothSendAuthenticationResponseEx( HANDLE handle_radio, BLUETOO
done: ReleaseSRWLockShared( &bluetooth_auth_lock ); + return ret; + } + +DWORD WINAPI BluetoothAuthenticateDeviceEx( HWND parent, HANDLE handle_radio, BLUETOOTH_DEVICE_INFO *device_info, + BLUETOOTH_OOB_DATA_INFO *oob_info, + AUTHENTICATION_REQUIREMENTS auth_req ) +{ + OVERLAPPED ovl = {0}; + BTH_ADDR device_addr; + BOOL success; + DWORD ret, bytes; + + FIXME( "(%p, %p, %p, %p, %d): semi-stub!\n", parent, handle_radio, device_info, oob_info, auth_req ); + + if (!device_info || auth_req < MITMProtectionNotRequired || auth_req > MITMProtectionNotDefined) + return ERROR_INVALID_PARAMETER; + if (device_info->dwSize != sizeof( *device_info )) + return ERROR_REVISION_MISMATCH; + + TRACE( "Initiating pairing with %s\n", debugstr_addr( device_info->Address.rgBytes ) ); + if (!handle_radio) + { + BLUETOOTH_FIND_RADIO_PARAMS find_params = {.dwSize = sizeof( find_params )}; + HBLUETOOTH_RADIO_FIND radio_find; + + radio_find = BluetoothFindFirstRadio( &find_params, &handle_radio ); + ret = GetLastError(); + if (!radio_find) + return ret == ERROR_NO_MORE_ITEMS ? ERROR_NOT_FOUND : ret; + do { + ret = BluetoothAuthenticateDeviceEx( parent, handle_radio, device_info, oob_info, auth_req ); + CloseHandle( handle_radio ); + if (!ret || ret == ERROR_NO_MORE_ITEMS) + break; + } while (BluetoothFindNextRadio( radio_find, &handle_radio )); + + BluetoothFindRadioClose( radio_find ); + return ret; + } + + ovl.hEvent = CreateEventW( NULL, TRUE, FALSE, NULL ); + 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 ); + ret = ERROR_SUCCESS; + if (!success) + { + ret = GetLastError(); + if (ret == ERROR_IO_PENDING) + ret = GetOverlappedResult( handle_radio, &ovl, &bytes, TRUE ); + CloseHandle( ovl.hEvent ); + } + return ret; }
diff --git a/dlls/bthprops.cpl/bthprops.cpl.spec b/dlls/bthprops.cpl/bthprops.cpl.spec index c7606604821..37b52b7f39f 100644 --- a/dlls/bthprops.cpl/bthprops.cpl.spec +++ b/dlls/bthprops.cpl/bthprops.cpl.spec @@ -1,6 +1,6 @@ @ stub BluetoothAddressToString @ stub BluetoothAuthenticateDevice -@ stub BluetoothAuthenticateDeviceEx +@ stdcall -import BluetoothAuthenticateDeviceEx(ptr ptr ptr ptr long) @ stub BluetoothAuthenticateMultipleDevices @ stub BluetoothAuthenticationAgent @ stub BluetoothDisconnectDevice diff --git a/include/bluetoothapis.h b/include/bluetoothapis.h index b97967ec81f..fbc2fca5479 100644 --- a/include/bluetoothapis.h +++ b/include/bluetoothapis.h @@ -140,6 +140,16 @@ typedef enum _BLUETOOTH_AUTHENTICATION_REQUIREMENTS{ BLUETOOTH_MITM_ProtectionNotDefined = 0xff, } BLUETOOTH_AUTHENTICATION_REQUIREMENTS;
+typedef enum _AUTHENTICATION_REQUIREMENTS { + MITMProtectionNotRequired = 0x00, + MITMProtectionRequired = 0x01, + MITMProtectionNotRequiredBonding = 0x02, + MITMProtectionRequiredBonding = 0x03, + MITMProtectionNotRequiredGeneralBonding = 0x04, + MITMProtectionRequiredGeneralBonding = 0x05, + MITMProtectionNotDefined = 0xff +} AUTHENTICATION_REQUIREMENTS; + typedef struct _BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS { BLUETOOTH_DEVICE_INFO deviceInfo; BLUETOOTH_AUTHENTICATION_METHOD authenticationMethod; @@ -243,6 +253,7 @@ typedef BOOL (CALLBACK *PFN_BLUETOOTH_ENUM_ATTRIBUTES_CALLBACK)( void *pvParam);
DWORD WINAPI BluetoothAuthenticateDevice(HWND, HANDLE, BLUETOOTH_DEVICE_INFO *, WCHAR *, ULONG); +DWORD WINAPI BluetoothAuthenticateDeviceEx(HWND, HANDLE, BLUETOOTH_DEVICE_INFO *, BLUETOOTH_OOB_DATA_INFO *, AUTHENTICATION_REQUIREMENTS); DWORD WINAPI BluetoothAuthenticateMultipleDevices(HWND, HANDLE, DWORD, BLUETOOTH_DEVICE_INFO *); BOOL WINAPI BluetoothDisplayDeviceProperties(HWND, BLUETOOTH_DEVICE_INFO *); BOOL WINAPI BluetoothEnableDiscovery(HANDLE, BOOL);
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
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/tests/Makefile.in | 1 + dlls/bluetoothapis/tests/device.c | 211 ++++++++++++++++++++++++++- dlls/bluetoothapis/tests/resource.h | 22 +++ dlls/bluetoothapis/tests/resource.rc | 32 ++++ 4 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 dlls/bluetoothapis/tests/resource.h create mode 100644 dlls/bluetoothapis/tests/resource.rc
diff --git a/dlls/bluetoothapis/tests/Makefile.in b/dlls/bluetoothapis/tests/Makefile.in index 601d70b9636..dad13359591 100644 --- a/dlls/bluetoothapis/tests/Makefile.in +++ b/dlls/bluetoothapis/tests/Makefile.in @@ -4,4 +4,5 @@ IMPORTS = bluetoothapis user32 SOURCES = \ device.c \ radio.c \ + resource.rc \ sdp.c diff --git a/dlls/bluetoothapis/tests/device.c b/dlls/bluetoothapis/tests/device.c index d608397826b..8753d675b46 100644 --- a/dlls/bluetoothapis/tests/device.c +++ b/dlls/bluetoothapis/tests/device.c @@ -31,6 +31,8 @@ #include <wine/debug.h> #include <wine/test.h>
+#include "resource.h" + extern void test_for_all_radios( const char *file, int line, void (*test)( HANDLE radio, void *data ), void *data );
static const char *debugstr_bluetooth_address( const BYTE *addr ) @@ -38,6 +40,9 @@ static const char *debugstr_bluetooth_address( const BYTE *addr ) return wine_dbg_sprintf( "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] ); }
+static DWORD(WINAPI *pBluetoothAuthenticateDeviceEx)( HWND, HANDLE, BLUETOOTH_DEVICE_INFO *, + BLUETOOTH_OOB_DATA_INFO *, AUTHENTICATION_REQUIREMENTS ); + static const char *debugstr_BLUETOOTH_DEVICE_INFO( const BLUETOOTH_DEVICE_INFO *info ) { WCHAR last_seen[256]; @@ -309,9 +314,6 @@ static void test_BluetoothRegisterForAuthenticationEx( void ) err = BluetoothRegisterForAuthenticationEx( &info, &hreg, NULL, NULL ); ok( err == ERROR_INVALID_PARAMETER, "%lu != %d\n", err, ERROR_INVALID_PARAMETER );
- auth_events[0] = CreateEventA( NULL, FALSE, FALSE, NULL ); - auth_events[1] = CreateEventA( NULL, FALSE, FALSE, NULL ); - BluetoothEnableIncomingConnections( NULL, TRUE ); BluetoothEnableDiscovery( NULL, TRUE );
@@ -415,8 +417,207 @@ static void test_BluetoothRemoveDevice( void ) test_for_all_radios( __FILE__, __LINE__, test_radio_BluetoothRemoveDevice, NULL ); }
+static BLUETOOTH_DEVICE_INFO *devices; + +static SIZE_T find_devices( int line, HANDLE radio ) +{ + BLUETOOTH_DEVICE_SEARCH_PARAMS params = {0}; + HBLUETOOTH_DEVICE_FIND find; + SIZE_T devices_len; + DWORD ret; + + devices_len = 1; + params.dwSize = sizeof( params ); + params.cTimeoutMultiplier = 5; + params.fIssueInquiry = TRUE; + params.fReturnAuthenticated = FALSE; + params.fReturnRemembered = FALSE; + params.fReturnUnknown = TRUE; + params.fReturnConnected = TRUE; + params.hRadio = radio; + + devices = calloc( 1, sizeof( *devices ) ); + devices->dwSize = sizeof( *devices ); + find = BluetoothFindFirstDevice( ¶ms, devices ); + ret = GetLastError(); + ok_( __FILE__, line )( !!find || ret == ERROR_NO_MORE_ITEMS, "BluetoothFindFirstDevice failed: %lu\n", ret ); + + if (!find) + { + devices_len = 0; + free( devices ); + return devices_len; + } + + for (;;) + { + BOOL success; + BLUETOOTH_DEVICE_INFO info = {.dwSize = sizeof( info )}; + + success = BluetoothFindNextDevice( find, &info ); + ret = GetLastError(); + ok_( __FILE__, line )( success || ret == ERROR_NO_MORE_ITEMS, "BluetoothFindNextDevice failed: %lu\n", ret ); + if (!success) + break; + if (info.fAuthenticated) + continue; + devices = realloc( devices, (devices_len + 1) * sizeof( *devices ) ); + devices[devices_len++] = info; + } + + BluetoothFindDeviceClose( find ); + return devices_len; +} + +static BLUETOOTH_ADDRESS selected_device; +static SIZE_T devices_len; +static HANDLE device_selected_event; + +static CALLBACK INT_PTR dialog_proc( HWND dialog, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + switch (msg) + { + case WM_INITDIALOG: + { + HWND devices_list = GetDlgItem( dialog, IDC_CBO_DEVICES ); + WCHAR device_desc[300]; + SIZE_T i; + + for (i = 0; i < devices_len; i++) + { + BYTE *addr = devices[i].Address.rgBytes; + swprintf( device_desc, ARRAY_SIZE( device_desc ), L"%s (%02x:%02x:%02x:%02x:%02x:%02x)", devices[i].szName, + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] ); + SendMessageW( devices_list, CB_ADDSTRING, 0, (LPARAM)device_desc ); + } + return TRUE; + } + case WM_COMMAND: + switch (LOWORD( wparam )) + { + case IDC_CBO_DEVICES: + { + if (HIWORD( wparam ) == CBN_SELCHANGE) + { + int idx = SendMessageA( (HWND)lparam, CB_GETCURSEL, 0, 0 ); + if (idx != CB_ERR) + selected_device = devices[idx].Address; + SetEvent(device_selected_event); + EndDialog( dialog, 0 ); + return TRUE; + } + break; + } + case IDCANCEL: + SetEvent(device_selected_event); + EndDialog( dialog, 0 ); + return TRUE; + default: + break; + } + default: + break; + } + + return FALSE; +} + +static BOOL get_remote_device_interactive( int line, HANDLE radio, BLUETOOTH_ADDRESS *device_addr ) +{ + devices_len = find_devices( line, radio ); + if (!devices_len) + return FALSE; + + selected_device.ullLong = 0; + + DialogBoxA( NULL, MAKEINTRESOURCEA( IDD_DEVICESDLG ), NULL, dialog_proc ); + WaitForSingleObject( device_selected_event, INFINITE ); + *device_addr = selected_device; + free( devices ); + return !!selected_device.ullLong; +} + +static void test_radio_BluetoothAuthenticateDeviceEx( HANDLE radio, void *data ) +{ + HBLUETOOTH_AUTHENTICATION_REGISTRATION auth_reg; + BLUETOOTH_DEVICE_INFO device_info = {0}; + DWORD ret; + + device_info.dwSize = sizeof( device_info ); + if (!get_remote_device_interactive( __LINE__, radio, &device_info.Address )) + { + skip( "Could not select device.\n" ); + return; + } + + ret = pBluetoothAuthenticateDeviceEx( NULL, radio, &device_info, NULL, 0xdeadbeef ); + ok( ret == ERROR_INVALID_PARAMETER, "%lu != %d\n", ret, ERROR_INVALID_PARAMETER ); + + /* Test authenticating without a registered auth callback, which triggers a wizard UI. */ + ret = pBluetoothAuthenticateDeviceEx( NULL, radio, &device_info, NULL, MITMProtectionRequiredGeneralBonding ); + ok( !ret, "BluetoothAuthenticateDeviceEx failed: %lu\n", ret ); + + memset( &device_info, 0, sizeof( device_info ) ); + device_info.dwSize = sizeof( device_info ); + if (!get_remote_device_interactive( __LINE__, radio, &device_info.Address )) + { + skip( "Could not select device.\n" ); + return; + } + ret = BluetoothRegisterForAuthenticationEx( &device_info, &auth_reg, auth_ex_callback, NULL ); + ok( !ret, "BluetoothRegisterForAuthenticationEx failed: %lu\n", ret ); + if (ret) + { + skip( "BluetoothRegisterForAuthenticationEx failed.\n" ); + return; + } + /* Test authentication with a registered callback. */ + ret = pBluetoothAuthenticateDeviceEx( NULL, radio, &device_info, NULL, MITMProtectionRequiredGeneralBonding ); + ok( !ret, "BluetoothAuthenticateDeviceEx failed: %lu\n", ret ); + if (ret) + { + BluetoothUnregisterAuthentication( auth_reg ); + skip( "BluetoothAuthenticateDeviceEx failed.\n" ); + return; + } + ret = WaitForSingleObject( auth_events[0], 60000 ); + ok ( !ret, "WaitForSginleObject value: %lu\n", ret ); + + BluetoothUnregisterAuthentication( auth_reg ); +} + +static void test_BluetoothAuthenticateDeviceEx( void ) +{ + DWORD ret; + + if (!pBluetoothAuthenticateDeviceEx) + { + skip( "BluetoothAuthenticateDeviceEx is not available.\n" ); + return; + } + + ret = pBluetoothAuthenticateDeviceEx( NULL, NULL, NULL, NULL, 0xdeadbeef ); + ok( ret == ERROR_INVALID_PARAMETER, "%lu != %d\n", ret, ERROR_INVALID_PARAMETER ); + ret = pBluetoothAuthenticateDeviceEx( NULL, NULL, NULL, NULL, MITMProtectionRequiredGeneralBonding ); + ok( ret == ERROR_INVALID_PARAMETER, "%lu != %d\n", ret, ERROR_INVALID_PARAMETER ); + + if (!winetest_interactive) + return; + + device_selected_event = CreateEventA( NULL, FALSE, FALSE, NULL ); + test_for_all_radios( __FILE__, __LINE__, test_radio_BluetoothAuthenticateDeviceEx, NULL ); + + CloseHandle( device_selected_event ); +} + START_TEST( device ) { + auth_events[0] = CreateEventA( NULL, FALSE, FALSE, NULL ); + auth_events[1] = CreateEventA( NULL, FALSE, FALSE, NULL ); + + pBluetoothAuthenticateDeviceEx = + (void *)GetProcAddress( LoadLibraryA( "bthprops.cpl" ), "BluetoothAuthenticateDeviceEx" ); + test_BluetoothFindFirstDevice(); test_BluetoothFindDeviceClose(); test_BluetoothFindNextDevice(); @@ -424,5 +625,9 @@ START_TEST( device ) if (winetest_interactive) test_BluetoothRegisterForAuthenticationEx(); test_BluetoothSendAuthenticationResponseEx(); + test_BluetoothAuthenticateDeviceEx(); test_BluetoothRemoveDevice(); + + CloseHandle( auth_events[0] ); + CloseHandle( auth_events[1] ); } diff --git a/dlls/bluetoothapis/tests/resource.h b/dlls/bluetoothapis/tests/resource.h new file mode 100644 index 00000000000..e61db91525a --- /dev/null +++ b/dlls/bluetoothapis/tests/resource.h @@ -0,0 +1,22 @@ +/* + * 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 + */ + +#define IDD_DEVICESDLG 101 + +#define IDC_CBO_DEVICES 102 +#define IDC_STATIC -1 diff --git a/dlls/bluetoothapis/tests/resource.rc b/dlls/bluetoothapis/tests/resource.rc new file mode 100644 index 00000000000..e19323272c1 --- /dev/null +++ b/dlls/bluetoothapis/tests/resource.rc @@ -0,0 +1,32 @@ +/* + * 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" + +#include "windef.h" +#include "winuser.h" + +IDD_DEVICESDLG DIALOG 260, 200, 300, 120 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg" +CAPTION "Select Device" +{ + DEFPUSHBUTTON "Cancel", IDCANCEL, 120, 100, 50, 14 + LTEXT "Device", IDC_STATIC, 10, 10, 25, 8 + COMBOBOX IDC_CBO_DEVICES, 40, 8, 250, 60, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN | CBS_HASSTRINGS +}
``` + 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; + } ```
Usually we want to keep code in W instead of doing a conversion. (And you already have a reference to "MessageBoxW"...)
I'm also tempted to advocate for using FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE), although maybe it's debatable whether avoiding the statically sized buffer or using that function's peculiar printf is an advantage...
On Fri May 2 18:28:11 2025 +0000, Elizabeth Figura wrote:
+ 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; + }
Usually we want to keep code in W instead of doing a conversion. (And you already have a reference to "MessageBoxW"...) I'm also tempted to advocate for using FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE), although maybe it's debatable whether avoiding the statically sized buffer or using that function's peculiar printf is an advantage...
Change to using the W variants, thanks.
I'm also tempted to advocate for using FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE), although maybe it's debatable whether avoiding the statically sized buffer or using that function's peculiar printf is an advantage...
I'm okay with it, but we'll need to add an extra function that only gets used once. Maybe we could use it once I implement `BluetoothSelectDevices` as well?