Add `IOCTL_WINEBTH_RADIO_START_AUTH`, which gets used to implement `BluetoothAuthenticateDeviceEx`.
-- v7: 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. winebth.sys: Implement IOCTL_WINEBTH_RADIO_START_AUTH.
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
squash authdevice. --- 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 +}
On Thu May 1 13:50:50 2025 +0000, Elizabeth Figura wrote:
+ snprintf( msg, sizeof( msg ), + "Pair with \"%s\" (%02X:%02X:%02X:%02X:%02X:%02X) using the following PIN/passkey value: %lu?", + request->device_info.name, addr.rgBytes[0], addr.rgBytes[1], addr.rgBytes[2], addr.rgBytes[3], + addr.rgBytes[4], addr.rgBytes[5], request->numeric_value_or_passkey ); + ret = MessageBoxA( NULL, msg, "Confirm pairing", MB_YESNO );
Both of these strings look like they should be translated.
The strings should be i18n-ed in the latest version, thanks.
v3:
* Make `IOCTL_WINEBTH_RADIO_START_AUTH` an asynchronous request, to fix a potential deadlock with the DBus event dispatch loop. Accordingly, use OVERLAPPED I/O to perform the IOCTL in `BluetoothAuthenticateDeviceEx`. * Internationalize strings used by the pairing wizard.