Skip to content

Commit 6338520

Browse files
committed
Merge branch 'win-prompt'
2 parents 7eab96e + 23a1bbb commit 6338520

File tree

5 files changed

+118
-31
lines changed

5 files changed

+118
-31
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## 1.6.1
4+
- Fix accept/deny prompt on Windows
5+
36
## 1.6.0
47

58
- Prompt to accept/deny a host which is not explicitly allowed

Cargo.lock

+39-29
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bitbox-bridge/Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "bitbox-bridge"
33
# If you bump this, also change the ProductCode in bitbox-bridge/release/windows/wix/Product.wxs.
4-
version = "1.6.0"
4+
version = "1.6.1"
55
authors = ["Niklas Claesson <[email protected]>"]
66
edition = "2021"
77
license = "Apache-2.0"
@@ -22,6 +22,9 @@ warp = "0.3.7"
2222
tera = "1.20"
2323
uuid = { version = "1.10.0", features = ["v4"] }
2424

25+
[target.'cfg(windows)'.dependencies]
26+
windows-sys = { version = "0.59.0", features = ["Win32", "Win32_System", "Win32_UI", "Win32_System_RemoteDesktop", "Win32_UI_WindowsAndMessaging"] }
27+
2528
[dependencies.u2fframing]
2629
version = "0.1"
2730
path = "../u2fframing"

bitbox-bridge/release/windows/wix/Product.wxs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
33

44
<!-- ProductCode should change with every release -->
5-
<?define ProductCode = "{de37ff56-5490-4e50-9fcb-5bb8e8597544}"?>
5+
<?define ProductCode = "{3fe2b668-f725-4884-92e9-7af6e513ccd9}"?>
66
<!-- UpgradeCode should stay the same foverever (this is the real ID of the app)-->
77
<?define UpgradeCode = "{dfe7eecb-5dc0-4c30-ba78-a9eff36efa31}"?>
88

bitbox-bridge/src/web.rs

+71
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,77 @@ impl AllowedHosts {
184184
}
185185
}
186186

187+
// On windows, we use a native system dialog to prompt the user.
188+
// The browser based prompt does not work as we can't launch a browser from the service (not allowed).
189+
//
190+
// We use WTSSendMessage as officially recommended (it recommends WTSSendMessageA, the ANSI-encoded
191+
// version, but WTSSendMessagW for UTF-16 also works):
192+
//
193+
// See https://learn.microsoft.com/en-us/windows/win32/services/interactive-services
194+
// > You can use the following techniques to interact with the user from a service on all supported versions of Windows:
195+
// > Display a dialog box in the user's session using the [WTSSendMessage](https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtssendmessagea) function.
196+
#[cfg(target_os = "windows")]
197+
async fn user_confirm(
198+
_confirm_state: Arc<ConfirmState>,
199+
message: String,
200+
_base_url: &str,
201+
) -> Result<bool, ()> {
202+
use std::os::windows::ffi::OsStrExt;
203+
use windows_sys::Win32::System::RemoteDesktop::{
204+
WTSGetActiveConsoleSessionId, WTSSendMessageW, WTS_CURRENT_SERVER_HANDLE,
205+
};
206+
use windows_sys::Win32::UI::WindowsAndMessaging::{IDYES, MB_YESNO, MESSAGEBOX_RESULT};
207+
208+
fn utf8_to_utf16(s: &str) -> Vec<u16> {
209+
std::ffi::OsStr::new(s)
210+
.encode_wide()
211+
.chain(std::iter::once(0))
212+
.collect()
213+
}
214+
215+
let title_c = utf8_to_utf16("BitBoxBridge");
216+
let message_c = utf8_to_utf16(&message);
217+
218+
let mut response: MESSAGEBOX_RESULT = 0;
219+
let timeout: u32 = 60; // 60 seconds
220+
221+
unsafe {
222+
// Need the active user session ID so the dialog is shown to the user.
223+
let current_session = WTSGetActiveConsoleSessionId();
224+
if current_session == 0xFFFFFFFF {
225+
// See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-wtsgetactiveconsolesessionid#return-value
226+
return Err(());
227+
}
228+
let result = WTSSendMessageW(
229+
WTS_CURRENT_SERVER_HANDLE,
230+
current_session,
231+
title_c.as_ptr(),
232+
// Each element is 2 bytes except, including the the null terminator
233+
(title_c.len() * 2) as u32,
234+
message_c.as_ptr(),
235+
(message_c.len() * 2) as u32,
236+
MB_YESNO,
237+
timeout,
238+
&mut response,
239+
1,
240+
);
241+
242+
if result == 0 {
243+
return Err(());
244+
}
245+
}
246+
247+
// Check if the user clicked 'Yes'
248+
Ok(response == IDYES)
249+
}
250+
251+
// On Linux/maCOS, we launch a browser with a prompt.
252+
//
253+
// On macOS, native dialogs don't work if there is no main window (we don't have one, it's a service).
254+
//
255+
// On linux, native dialogs would work, but we use the browser based solution here too as native
256+
// dialogs might not work in all distros/configs.
257+
#[cfg(not(target_os = "windows"))]
187258
async fn user_confirm(
188259
confirm_state: Arc<ConfirmState>,
189260
message: String,

0 commit comments

Comments
 (0)