Skip to content

Commit be6d69a

Browse files
committed
feat: finalize windows build
1 parent f497102 commit be6d69a

File tree

22 files changed

+985
-188
lines changed

22 files changed

+985
-188
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
.vscode/
44
._*
55
.DS_Store
6-
vpnc-script-win.js
76
*.example.json

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ serde = { version = "1.0.197", features = ["derive"] }
2323
serde_json = "1.0.114"
2424
home = "0.5.9"
2525
libc = "0.2.153"
26+
dunce = "1.0.4"

crates/openconnect-core/src/lib.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,41 @@ impl VpnClient {
8484
.map(|s| CString::new(s).unwrap())
8585
.map_or_else(|| DEFAULT_VPNCSCRIPT.as_ptr() as *const i8, |s| s.as_ptr());
8686

87-
let ret = openconnect_setup_tun_device(
88-
(*client).vpninfo,
89-
vpnc_script,
90-
std::ptr::null_mut(),
91-
);
87+
#[cfg(target_os = "windows")]
88+
{
89+
// currently use wintun on windows
90+
// https://gitlab.com/openconnect/openconnect-gui/-/blob/main/src/vpninfo.cpp?ref_type=heads#L407
91+
// TODO: investigate tap ip address allocation, since it works well in Openconnect-GUI
92+
let hostname = (*client).get_hostname();
93+
let ifname = hostname.and_then(|hostname| {
94+
let ifname = format!("tun_{}", hostname);
95+
CString::new(ifname).ok()
96+
});
97+
98+
if let Some(ifname) = ifname {
99+
let ret = openconnect_setup_tun_device(
100+
(*client).vpninfo,
101+
vpnc_script,
102+
ifname.as_ptr(),
103+
);
104+
// TODO: handle ret
105+
println!("setup_tun_device ret: {}", ret);
106+
} else {
107+
panic!("setup_tun_device failed: ifname is None");
108+
}
109+
}
92110

93-
// TODO: handle ret
94-
println!("setup_tun_device ret: {}", ret);
111+
#[cfg(not(target_os = "windows"))]
112+
{
113+
let ret = openconnect_setup_tun_device(
114+
(*client).vpninfo,
115+
vpnc_script,
116+
std::ptr::null(), // currently use tun/tap on linux
117+
);
118+
119+
// TODO: handle ret
120+
println!("setup_tun_device ret: {}", ret);
121+
}
95122
}
96123
}
97124
}

crates/openconnect-gui/src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ custom-protocol = ["tauri/custom-protocol"]
4747

4848
[target.'cfg(windows)'.dependencies]
4949
windows-sys = { workspace = true }
50+
dunce = { workspace = true }
Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
11
fn main() {
2-
// this requires signing the app
3-
let _windows_attributes = tauri_build::WindowsAttributes::new().app_manifest(
4-
r#"<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
2+
let profile = std::env::var("PROFILE").unwrap();
3+
4+
match profile.as_str() {
5+
"release" => {
6+
let mut windows = tauri_build::WindowsAttributes::new();
7+
8+
windows = windows.app_manifest(
9+
r#"<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
10+
<dependency>
11+
<dependentAssembly>
12+
<assemblyIdentity
13+
type="win32"
14+
name="Microsoft.Windows.Common-Controls"
15+
version="6.0.0.0"
16+
processorArchitecture="*"
17+
publicKeyToken="6595b64144ccf1df"
18+
language="*"
19+
/>
20+
</dependentAssembly>
21+
</dependency>
522
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
623
<security>
724
<requestedPrivileges>
8-
<requestedExecutionLevel level="requireAdministrator" uiAccess="true" />
25+
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
926
</requestedPrivileges>
1027
</security>
1128
</trustInfo>
12-
</assembly>
13-
"#,
14-
);
15-
16-
// this requires signing the app
17-
// tauri_build::try_build(tauri_build::Attributes::new().windows_attributes(windows_attributes))
18-
// .expect("error while building tauri application")
19-
20-
tauri_build::build();
29+
</assembly>"#,
30+
);
31+
tauri_build::try_build(tauri_build::Attributes::new().windows_attributes(windows))
32+
.expect("failed to run build script");
33+
}
34+
"debug" => {
35+
tauri_build::build();
36+
}
37+
_ => {}
38+
}
2139
}

crates/openconnect-gui/src-tauri/src/main.rs

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33

44
mod oidc;
55
mod state;
6-
use std::os::unix::fs::PermissionsExt;
76

8-
use openconnect_core::storage::{StoredConfigs, StoredConfigsJson};
7+
use openconnect_core::storage::{StoredConfigs, StoredConfigsJson, StoredServer};
98
use state::AppState;
109
use tauri::Manager;
1110

@@ -16,14 +15,12 @@ fn greet(name: &str) -> String {
1615
}
1716

1817
#[tauri::command(rename_all = "snake_case")]
19-
async fn connect(
18+
async fn connect_with_password(
2019
app_state: tauri::State<'_, AppState>,
21-
server: String,
22-
username: String,
23-
password: String,
20+
server_name: String,
2421
) -> anyhow::Result<(), String> {
2522
app_state
26-
.connect_with_user_pass(&server, &username, &password)
23+
.connect_with_user_pass(&server_name)
2724
.await
2825
.map_err(|e| e.to_string())
2926
}
@@ -64,6 +61,20 @@ async fn get_stored_configs() -> anyhow::Result<StoredConfigsJson, String> {
6461
Ok(stored_configs.into())
6562
}
6663

64+
#[tauri::command]
65+
async fn upsert_stored_server(server: StoredServer) -> anyhow::Result<(), String> {
66+
let mut stored_configs = StoredConfigs::new();
67+
stored_configs
68+
.read_from_file()
69+
.await
70+
.map_err(|e| e.to_string())?;
71+
stored_configs
72+
.upsert_server(server)
73+
.await
74+
.map_err(|e| e.to_string())?;
75+
Ok(())
76+
}
77+
6778
fn main() {
6879
#[cfg(target_os = "linux")]
6980
{
@@ -92,40 +103,59 @@ fn main() {
92103
.body(b"Authenticated, close this window and return to the application.".to_vec())
93104
})
94105
.setup(|app| {
95-
#[cfg(not(target_os = "windows"))]
96-
{
97-
let resource_path = app
98-
.path_resolver()
99-
.resolve_resource("vpnc-scripts/vpnc-script")
100-
.expect("failed to resolve resource");
101-
102-
let file = std::fs::OpenOptions::new()
103-
.write(false)
104-
.create(false)
105-
.append(false)
106-
.read(true)
107-
.open(resource_path)
108-
.expect("failed to open file");
109-
110-
let permissions = file.metadata().unwrap().permissions();
111-
let is_executable = permissions.mode() & 0o111 != 0;
112-
if !is_executable {
113-
let mut permissions = permissions;
114-
permissions.set_mode(0o755);
115-
file.set_permissions(permissions).unwrap();
106+
let vpnc_script = {
107+
#[cfg(target_os = "windows")]
108+
{
109+
let resource_path = app
110+
.path_resolver()
111+
.resolve_resource("vpnc-script-win.js")
112+
.expect("failed to resolve resource");
113+
114+
dunce::canonicalize(resource_path)
115+
.expect("failed to canonicalize path")
116+
.to_string_lossy()
117+
.to_string()
116118
}
117-
}
118119

119-
AppState::handle(app);
120+
#[cfg(not(target_os = "windows"))]
121+
{
122+
use std::os::unix::fs::PermissionsExt;
123+
let resource_path = app
124+
.path_resolver()
125+
.resolve_resource("vpnc-script")
126+
.expect("failed to resolve resource");
127+
128+
let file = std::fs::OpenOptions::new()
129+
.write(false)
130+
.create(false)
131+
.append(false)
132+
.read(true)
133+
.open(resource_path)
134+
.expect("failed to open file");
135+
136+
let permissions = file.metadata().unwrap().permissions();
137+
let is_executable = permissions.mode() & 0o111 != 0;
138+
if !is_executable {
139+
let mut permissions = permissions;
140+
permissions.set_mode(0o755);
141+
file.set_permissions(permissions).unwrap();
142+
}
143+
144+
resource_path.to_string_lossy().to_string()
145+
}
146+
};
147+
148+
AppState::handle_with_vpnc_script(app, &vpnc_script);
120149
Ok(())
121150
})
122151
.invoke_handler(tauri::generate_handler![
123152
greet,
124-
connect,
125153
disconnect,
126154
trigger_state_retrieve,
155+
get_stored_configs,
156+
upsert_stored_server,
157+
connect_with_password,
127158
connect_with_oidc,
128-
get_stored_configs
129159
])
130160
.run(tauri::generate_context!())
131161
.expect("error while running tauri application");

crates/openconnect-gui/src-tauri/src/state.rs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ impl From<Status> for StatusPayload {
4040
pub struct AppState {
4141
pub(crate) event_tx: Sender<VpnEvent>,
4242
pub(crate) client: RwLock<Option<Arc<VpnClient>>>,
43+
#[allow(dead_code)]
44+
pub(crate) vpnc_sciprt: String,
4345
}
4446

4547
impl AppState {
46-
pub fn handle(app: &mut tauri::App) {
48+
pub fn handle_with_vpnc_script(app: &mut tauri::App, vpnc_scipt: &str) {
4749
let (event_tx, mut event_rx) = channel::<VpnEvent>(100);
48-
let app_state = AppState::new(event_tx);
50+
let app_state = AppState::new(event_tx, vpnc_scipt);
4951
app.manage(app_state);
5052

5153
let handle = app.app_handle();
@@ -79,18 +81,25 @@ impl AppState {
7981
}
8082
}
8183

82-
pub async fn connect_with_user_pass(
83-
&self,
84-
server: &str,
85-
username: &str,
86-
password: &str,
87-
) -> anyhow::Result<()> {
88-
let config = ConfigBuilder::default().loglevel(LogLevel::Info).build()?;
84+
pub async fn connect_with_user_pass(&self, server_name: &str) -> anyhow::Result<()> {
85+
let mut stored_server = StoredConfigs::default();
86+
let stored_server = stored_server
87+
.read_from_file()
88+
.await?
89+
.get_server_as_password(server_name)
90+
.ok_or(anyhow::anyhow!("Server not found"))?;
91+
92+
let mut config = ConfigBuilder::default();
93+
94+
#[cfg(not(target_os = "windows"))]
95+
let mut config = config.vpncscript(&self.vpnc_sciprt);
96+
97+
let config = config.loglevel(LogLevel::Info).build()?;
8998

9099
let entrypoint = EntrypointBuilder::new()
91-
.server(server)
92-
.username(username)
93-
.password(password)
100+
.server(&stored_server.server)
101+
.username(&stored_server.username)
102+
.password(&stored_server.password)
94103
.enable_udp(true)
95104
.build()?;
96105

@@ -148,7 +157,12 @@ impl AppState {
148157
.await
149158
.ok_or(anyhow::anyhow!("Failed to obtain cookie from server"))?;
150159

151-
let config = ConfigBuilder::default().loglevel(LogLevel::Info).build()?;
160+
let mut config = ConfigBuilder::default();
161+
162+
#[cfg(not(target_os = "windows"))]
163+
let mut config = config.vpncscript(&self.vpnc_sciprt);
164+
165+
let config = config.loglevel(LogLevel::Info).build()?;
152166

153167
let entrypoint = EntrypointBuilder::new()
154168
.server(&stored_server.server)
@@ -189,10 +203,11 @@ impl AppState {
189203
Ok(())
190204
}
191205

192-
pub fn new(event_tx: Sender<VpnEvent>) -> Self {
206+
pub fn new(event_tx: Sender<VpnEvent>, vpnc_scipt: &str) -> Self {
193207
Self {
194208
event_tx,
195209
client: RwLock::new(None),
210+
vpnc_sciprt: vpnc_scipt.to_string(),
196211
}
197212
}
198213
}

crates/openconnect-gui/src-tauri/tauri.conf.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
"windows": [
3636
{
3737
"title": "openconnect-gui",
38-
"width": 800,
39-
"height": 600,
38+
"width": 900,
39+
"height": 800,
4040
"transparent": true,
4141
"decorations": false
4242
}
@@ -56,7 +56,9 @@
5656
"icons/icon.ico"
5757
],
5858
"resources": [
59-
"vpnc-scripts/*"
59+
"vpnc-script",
60+
"vpnc-script-win.js",
61+
"wintun.dll"
6062
]
6163
}
6264
}
File renamed without changes.

0 commit comments

Comments
 (0)