1use std::collections::HashMap;
2use std::process::Command;
3use std::sync::mpsc;
4use tauri::{plugin::PluginApi, AppHandle, Runtime};
5use zbus::names::InterfaceName;
6use zbus::zvariant::Value;
7
8use crate::error::Result;
9use crate::models::*;
10
11impl<R: Runtime> VSKNetworkManager<'static, R> {
12 fn get_wifi_icon(strength: u8) -> String {
14 match strength {
15 0..=25 => "network-wireless-signal-weak-symbolic".to_string(),
16 26..=50 => "network-wireless-signal-ok-symbolic".to_string(),
17 51..=75 => "network-wireless-signal-good-symbolic".to_string(),
18 76..=100 => "network-wireless-signal-excellent-symbolic".to_string(),
19 _ => "network-wireless-signal-none-symbolic".to_string(),
20 }
21 }
22
23 fn get_wired_icon(is_connected: bool) -> String {
24 if is_connected {
25 "network-wired-symbolic".to_string()
26 } else {
27 "network-offline-symbolic".to_string()
28 }
29 }
30
31 pub async fn new(app: AppHandle<R>) -> Result<Self> {
33 let connection = zbus::blocking::Connection::system()?;
34 let proxy = zbus::blocking::fdo::PropertiesProxy::builder(&connection)
35 .destination("org.freedesktop.NetworkManager")?
36 .path("/org/freedesktop/NetworkManager")?
37 .build()?;
38
39 Ok(Self {
40 connection,
41 proxy,
42 app,
43 })
44 }
45
46 fn has_internet_connectivity() -> bool {
47 Command::new("ping")
48 .arg("-c")
49 .arg("1")
50 .arg("8.8.8.8")
51 .output()
52 .map(|output| output.status.success())
53 .unwrap_or(false)
54 }
55
56 pub fn get_current_network_state(&self) -> Result<NetworkInfo> {
57 let active_connections_variant = self.proxy.get(
59 InterfaceName::from_static_str_unchecked("org.freedesktop.NetworkManager"),
60 "ActiveConnections",
61 )?;
62
63 match active_connections_variant.downcast_ref() {
65 Some(Value::Array(arr)) if !arr.is_empty() => {
66 match arr[0] {
68 zbus::zvariant::Value::ObjectPath(ref path) => {
69 let properties_proxy =
72 zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
73 .destination("org.freedesktop.NetworkManager")?
74 .path(path)?
75 .build()?;
76
77 let devices_variant = properties_proxy.get(
78 InterfaceName::from_static_str_unchecked(
79 "org.freedesktop.NetworkManager.Connection.Active",
80 ),
81 "Devices",
82 )?;
83
84 let device_path = match devices_variant.downcast_ref() {
86 Some(Value::Array(device_arr)) if !device_arr.is_empty() => {
87 match device_arr[0] {
88 zbus::zvariant::Value::ObjectPath(ref dev_path) => {
89 dev_path.clone()
90 }
91 _ => return Ok(NetworkInfo::default()),
92 }
93 }
94 _ => return Ok(NetworkInfo::default()),
95 };
96
97 let device_properties_proxy =
100 zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
101 .destination("org.freedesktop.NetworkManager")?
102 .path(&device_path)?
103 .build()?;
104
105 let connection_type = device_properties_proxy.get(
106 InterfaceName::from_static_str_unchecked(
107 "org.freedesktop.NetworkManager.Device",
108 ),
109 "DeviceType",
110 )?;
111
112 let state_variant = properties_proxy.get(
113 InterfaceName::from_static_str_unchecked(
114 "org.freedesktop.NetworkManager.Connection.Active",
115 ),
116 "State",
117 )?;
118
119 let is_connected = match state_variant.downcast_ref() {
120 Some(zbus::zvariant::Value::U32(state)) => *state == 2, _ => false,
122 };
123
124 let connection_type_str = match connection_type.downcast_ref() {
126 Some(zbus::zvariant::Value::U32(device_type)) => match device_type {
127 1 => "Ethernet".to_string(),
128 2 => "WiFi".to_string(),
129 _ => "Unknown".to_string(),
130 },
131 _ => "Unknown".to_string(),
132 };
133
134 let mut network_info = NetworkInfo {
136 name: "Unknown".to_string(),
137 ssid: "Unknown".to_string(),
138 connection_type: connection_type_str.clone(),
139 icon: "network-offline-symbolic".to_string(),
140 ip_address: "0.0.0.0".to_string(),
141 mac_address: "00:00:00:00:00:00".to_string(),
142 signal_strength: 0,
143 security_type: WiFiSecurityType::None,
144 is_connected: is_connected && Self::has_internet_connectivity(),
145 };
146
147 let hw_address_variant = device_properties_proxy.get(
148 InterfaceName::from_static_str_unchecked(
149 "org.freedesktop.NetworkManager.Device",
150 ),
151 "HwAddress",
152 )?;
153
154 network_info.mac_address = match hw_address_variant.downcast_ref() {
155 Some(zbus::zvariant::Value::Str(s)) => s.to_string(),
156 _ => "00:00:00:00:00:00".to_string(),
157 };
158
159 if connection_type_str == "WiFi" {
161 let wireless_properties_proxy =
164 zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
165 .destination("org.freedesktop.NetworkManager")?
166 .path(&device_path)?
167 .build()?;
168
169 let active_ap_path = wireless_properties_proxy.get(
170 InterfaceName::from_static_str_unchecked(
171 "org.freedesktop.NetworkManager.Device.Wireless",
172 ),
173 "ActiveAccessPoint",
174 )?;
175
176 if let Some(zbus::zvariant::Value::ObjectPath(ap_path)) =
177 active_ap_path.downcast_ref()
178 {
179 let _ap_proxy = zbus::blocking::Proxy::new(
180 &self.connection,
181 "org.freedesktop.NetworkManager",
182 ap_path,
183 "org.freedesktop.NetworkManager.AccessPoint",
184 )?;
185
186 let ap_properties_proxy =
189 zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
190 .destination("org.freedesktop.NetworkManager")?
191 .path(ap_path)?
192 .build()?;
193
194 let ssid_variant = ap_properties_proxy.get(
195 InterfaceName::from_static_str_unchecked(
196 "org.freedesktop.NetworkManager.AccessPoint",
197 ),
198 "Ssid",
199 )?;
200
201 network_info.ssid = match ssid_variant.downcast_ref() {
202 Some(zbus::zvariant::Value::Array(ssid_bytes)) => {
203 let bytes: Vec<u8> = ssid_bytes
205 .iter()
206 .filter_map(|v| {
207 if let zbus::zvariant::Value::U8(b) = v {
208 Some(*b)
209 } else {
210 None
211 }
212 })
213 .collect();
214
215 String::from_utf8_lossy(&bytes).to_string()
216 }
217 _ => "Unknown".to_string(),
218 };
219 network_info.name = network_info.ssid.clone();
220
221 let strength_variant = ap_properties_proxy.get(
223 InterfaceName::from_static_str_unchecked(
224 "org.freedesktop.NetworkManager.AccessPoint",
225 ),
226 "Strength",
227 )?;
228
229 network_info.signal_strength = match strength_variant.downcast_ref()
230 {
231 Some(zbus::zvariant::Value::U8(s)) => *s,
232 _ => 0,
233 };
234
235 network_info.icon =
237 Self::get_wifi_icon(network_info.signal_strength);
238
239 let flags_variant = ap_properties_proxy.get(
241 InterfaceName::from_static_str_unchecked(
242 "org.freedesktop.NetworkManager.AccessPoint",
243 ),
244 "Flags",
245 )?;
246 let wpa_flags_variant = ap_properties_proxy.get(
247 InterfaceName::from_static_str_unchecked(
248 "org.freedesktop.NetworkManager.AccessPoint",
249 ),
250 "WpaFlags",
251 )?;
252 let rsn_flags_variant = ap_properties_proxy.get(
253 InterfaceName::from_static_str_unchecked(
254 "org.freedesktop.NetworkManager.AccessPoint",
255 ),
256 "RsnFlags",
257 )?;
258
259 let flags = if let Some(zbus::zvariant::Value::U32(f)) = flags_variant.downcast_ref() { *f } else { 0 };
260 let wpa = if let Some(zbus::zvariant::Value::U32(w)) = wpa_flags_variant.downcast_ref() { *w } else { 0 };
261 let rsn = if let Some(zbus::zvariant::Value::U32(r)) = rsn_flags_variant.downcast_ref() { *r } else { 0 };
262
263 let key_mgmt_variant = ap_properties_proxy.get(
265 InterfaceName::from_static_str_unchecked(
266 "org.freedesktop.NetworkManager.AccessPoint",
267 ),
268 "KeyMgmt",
269 );
270 let security_type = if let Ok(key_mgmt_variant) = key_mgmt_variant {
271 if let Some(zbus::zvariant::Value::Str(key_mgmt)) = key_mgmt_variant.downcast_ref() {
272 match key_mgmt.as_str() {
273 "none" => WiFiSecurityType::None,
274 "wpa-psk" => WiFiSecurityType::WpaPsk,
275 "wpa-eap" => WiFiSecurityType::WpaEap,
276 "sae" => WiFiSecurityType::Wpa3Psk,
277 _ => WiFiSecurityType::None,
278 }
279 } else {
280 if flags & 0x1 != 0 {
282 WiFiSecurityType::None
283 } else if flags & 0x2 != 0 {
284 WiFiSecurityType::Wep
285 } else if wpa != 0 && rsn == 0 {
286 WiFiSecurityType::WpaPsk
287 } else if rsn != 0 {
288 if wpa != 0 {
289 WiFiSecurityType::Wpa2Psk
290 } else {
291 WiFiSecurityType::Wpa3Psk
292 }
293 } else {
294 WiFiSecurityType::None
295 }
296 }
297 } else {
298 if flags & 0x1 != 0 {
300 WiFiSecurityType::None
301 } else if flags & 0x2 != 0 {
302 WiFiSecurityType::Wep
303 } else if wpa != 0 && rsn == 0 {
304 WiFiSecurityType::WpaPsk
305 } else if rsn != 0 {
306 if wpa != 0 {
307 WiFiSecurityType::Wpa2Psk
308 } else {
309 WiFiSecurityType::Wpa3Psk
310 }
311 } else {
312 WiFiSecurityType::None
313 }
314 };
315
316 network_info.security_type = security_type;
318
319 }
322 } else {
323 network_info.icon = Self::get_wired_icon(network_info.is_connected);
325 }
326 let ip4_config_path = device_properties_proxy.get(
328 InterfaceName::from_static_str_unchecked(
329 "org.freedesktop.NetworkManager.Device",
330 ),
331 "Ip4Config",
332 )?;
333
334 if let Some(zbus::zvariant::Value::ObjectPath(config_path)) =
336 ip4_config_path.downcast_ref()
337 {
338 let ip_config_properties_proxy =
340 zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
341 .destination("org.freedesktop.NetworkManager")?
342 .path(config_path)?
343 .build()?;
344
345 let addresses_variant = ip_config_properties_proxy.get(
346 InterfaceName::from_static_str_unchecked(
347 "org.freedesktop.NetworkManager.IP4Config",
348 ),
349 "Addresses",
350 )?;
351
352 if let Some(Value::Array(addr_arr)) = addresses_variant.downcast_ref() {
353 if let Some(Value::Array(ip_tuple)) = addr_arr.first() {
354 if ip_tuple.len() >= 1 {
355 if let Value::U32(ip_int) = &ip_tuple[0] {
356 use std::net::Ipv4Addr;
357 network_info.ip_address =
358 Ipv4Addr::from((*ip_int).to_be()).to_string();
359 }
360 }
361 }
362 }
363 }
364
365 Ok(network_info)
366 }
367 _ => Ok(NetworkInfo::default()),
368 }
369 }
370 _ => Ok(NetworkInfo::default()),
371 }
372 }
373
374 pub fn list_wifi_networks(&self) -> Result<Vec<NetworkInfo>> {
376 let devices_variant = self.proxy.get(
378 InterfaceName::from_static_str_unchecked("org.freedesktop.NetworkManager"),
379 "Devices",
380 )?;
381
382 let mut networks = Vec::new();
383 let current_network = self.get_current_network_state()?;
384
385 if let Some(zbus::zvariant::Value::Array(devices)) = devices_variant.downcast_ref() {
386 let device_values = devices.get();
388 for device in device_values {
389 if let zbus::zvariant::Value::ObjectPath(ref device_path) = device {
390 let device_props =
392 zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
393 .destination("org.freedesktop.NetworkManager")?
394 .path(device_path)?
395 .build()?;
396
397 let device_type_variant = device_props.get(
399 InterfaceName::from_static_str_unchecked(
400 "org.freedesktop.NetworkManager.Device",
401 ),
402 "DeviceType",
403 )?;
404
405 if let Some(zbus::zvariant::Value::U32(device_type)) =
407 device_type_variant.downcast_ref()
408 {
409 if device_type == &2u32 {
410 let wireless_props =
412 zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
413 .destination("org.freedesktop.NetworkManager")?
414 .path(device_path)?
415 .build()?;
416
417 let access_points_variant = wireless_props.get(
418 InterfaceName::from_static_str_unchecked(
419 "org.freedesktop.NetworkManager.Device.Wireless",
420 ),
421 "AccessPoints",
422 )?;
423
424 if let Some(zbus::zvariant::Value::Array(aps)) =
425 access_points_variant.downcast_ref()
426 {
427 let ap_values = aps.get();
429 for ap in ap_values {
430 if let zbus::zvariant::Value::ObjectPath(ref ap_path) = ap {
431 let ap_props = zbus::blocking::fdo::PropertiesProxy::builder(
432 &self.connection,
433 )
434 .destination("org.freedesktop.NetworkManager")?
435 .path(ap_path)?
436 .build()?;
437
438 let ssid_variant = ap_props.get(
440 InterfaceName::from_static_str_unchecked(
441 "org.freedesktop.NetworkManager.AccessPoint",
442 ),
443 "Ssid",
444 )?;
445
446 let ssid = match ssid_variant.downcast_ref() {
447 Some(zbus::zvariant::Value::Array(ssid_bytes)) => {
448 let bytes: Vec<u8> = ssid_bytes
450 .iter()
451 .filter_map(|v| {
452 if let zbus::zvariant::Value::U8(b) = v {
453 Some(*b)
454 } else {
455 None
456 }
457 })
458 .collect();
459
460 String::from_utf8_lossy(&bytes).to_string()
461 }
462 _ => "Unknown".to_string(),
463 };
464
465 let strength_variant = ap_props.get(
467 InterfaceName::from_static_str_unchecked(
468 "org.freedesktop.NetworkManager.AccessPoint",
469 ),
470 "Strength",
471 )?;
472
473 let strength = match strength_variant.downcast_ref() {
474 Some(zbus::zvariant::Value::U8(s)) => *s,
475 _ => 0,
476 };
477
478 let flags_variant = ap_props.get(
480 InterfaceName::from_static_str_unchecked(
481 "org.freedesktop.NetworkManager.AccessPoint",
482 ),
483 "Flags",
484 )?;
485 let wpa_flags_variant = ap_props.get(
486 InterfaceName::from_static_str_unchecked(
487 "org.freedesktop.NetworkManager.AccessPoint",
488 ),
489 "WpaFlags",
490 )?;
491 let rsn_flags_variant = ap_props.get(
492 InterfaceName::from_static_str_unchecked(
493 "org.freedesktop.NetworkManager.AccessPoint",
494 ),
495 "RsnFlags",
496 )?;
497
498 let flags = if let Some(zbus::zvariant::Value::U32(f)) = flags_variant.downcast_ref() { *f } else { 0 };
499 let wpa = if let Some(zbus::zvariant::Value::U32(w)) = wpa_flags_variant.downcast_ref() { *w } else { 0 };
500 let rsn = if let Some(zbus::zvariant::Value::U32(r)) = rsn_flags_variant.downcast_ref() { *r } else { 0 };
501
502 let key_mgmt_variant = ap_props.get(
504 InterfaceName::from_static_str_unchecked(
505 "org.freedesktop.NetworkManager.AccessPoint",
506 ),
507 "KeyMgmt",
508 );
509 let security_type = if let Ok(key_mgmt_variant) = key_mgmt_variant {
510 if let Some(zbus::zvariant::Value::Str(key_mgmt)) = key_mgmt_variant.downcast_ref() {
511 match key_mgmt.as_str() {
512 "none" => WiFiSecurityType::None,
513 "wpa-psk" => WiFiSecurityType::WpaPsk,
514 "wpa-eap" => WiFiSecurityType::WpaEap,
515 "sae" => WiFiSecurityType::Wpa3Psk,
516 _ => WiFiSecurityType::None,
517 }
518 } else {
519 if flags & 0x1 != 0 {
521 WiFiSecurityType::None
522 } else if flags & 0x2 != 0 {
523 WiFiSecurityType::Wep
524 } else if wpa != 0 && rsn == 0 {
525 WiFiSecurityType::WpaPsk
526 } else if rsn != 0 {
527 if wpa != 0 {
528 WiFiSecurityType::Wpa2Psk
529 } else {
530 WiFiSecurityType::Wpa3Psk
531 }
532 } else {
533 WiFiSecurityType::None
534 }
535 }
536 } else {
537 if flags & 0x1 != 0 {
539 WiFiSecurityType::None
540 } else if flags & 0x2 != 0 {
541 WiFiSecurityType::Wep
542 } else if wpa != 0 && rsn == 0 {
543 WiFiSecurityType::WpaPsk
544 } else if rsn != 0 {
545 if wpa != 0 {
546 WiFiSecurityType::Wpa2Psk
547 } else {
548 WiFiSecurityType::Wpa3Psk
549 }
550 } else {
551 WiFiSecurityType::None
552 }
553 };
554
555 let mac_address = match device_props.get(
556 InterfaceName::from_static_str_unchecked(
557 "org.freedesktop.NetworkManager.Device",
558 ),
559 "HwAddress",
560 )?.downcast_ref() {
561 Some(zbus::zvariant::Value::Str(s)) => s.to_string(),
562 _ => "00:00:00:00:00:00".to_string(),
563 };
564
565 let is_connected = current_network.ssid == ssid;
566
567 let network_info = NetworkInfo {
568 name: ssid.clone(),
569 ssid,
570 connection_type: "wifi".to_string(),
571 icon: Self::get_wifi_icon(strength),
572 ip_address: if is_connected {
573 current_network.ip_address.clone()
574 } else {
575 "0.0.0.0".to_string()
576 },
577 mac_address,
578 signal_strength: strength,
579 security_type,
580 is_connected,
581 };
582
583 if !networks.iter().any(|n: &NetworkInfo| n.ssid == network_info.ssid) {
584 networks.push(network_info);
585 }
586 }
587 }
588 }
589 }
590 }
591 }
592 }
593 }
594
595 networks.sort_by(|a, b| b.signal_strength.cmp(&a.signal_strength));
597
598 Ok(networks)
599 }
600
601 pub async fn connect_to_wifi(&self, config: WiFiConnectionConfig) -> Result<()> {
603 eprintln!("[network-manager] connect_to_wifi called: ssid='{}' security={:?} username={:?}",
605 config.ssid, config.security_type, config.username);
606
607 let mut connection_settings = HashMap::new();
609 let mut wifi_settings = HashMap::new();
610 let mut security_settings = HashMap::new();
611
612 let mut connection = HashMap::new();
614 connection.insert("id".to_string(), Value::from(config.ssid.clone()));
615 connection.insert("type".to_string(), Value::from("802-11-wireless"));
616 connection_settings.insert("connection".to_string(), connection);
617
618 wifi_settings.insert("ssid".to_string(), Value::from(config.ssid.clone()));
620 wifi_settings.insert("mode".to_string(), Value::from("infrastructure"));
621
622 match config.security_type {
624 WiFiSecurityType::None => {
625 }
627 WiFiSecurityType::Wep => {
628 security_settings.insert("key-mgmt".to_string(), Value::from("none"));
629 if let Some(password) = config.password.clone() {
630 security_settings.insert("wep-key0".to_string(), Value::from(password));
631 }
632 }
633 WiFiSecurityType::WpaPsk => {
634 security_settings.insert("key-mgmt".to_string(), Value::from("wpa-psk"));
635 if let Some(password) = config.password.clone() {
636 security_settings.insert("psk".to_string(), Value::from(password));
637 }
638 }
639 WiFiSecurityType::WpaEap => {
640 security_settings.insert("key-mgmt".to_string(), Value::from("wpa-eap"));
641 if let Some(password) = config.password.clone() {
642 security_settings.insert("password".to_string(), Value::from(password));
643 }
644 if let Some(username) = config.username.clone() {
645 security_settings.insert("identity".to_string(), Value::from(username));
646 }
647 }
648 WiFiSecurityType::Wpa2Psk => {
649 security_settings.insert("key-mgmt".to_string(), Value::from("wpa-psk"));
650 security_settings.insert("proto".to_string(), Value::from("rsn"));
651 if let Some(password) = config.password.clone() {
652 security_settings.insert("psk".to_string(), Value::from(password));
653 }
654 }
655 WiFiSecurityType::Wpa3Psk => {
656 security_settings.insert("key-mgmt".to_string(), Value::from("sae"));
657 if let Some(password) = config.password.clone() {
658 security_settings.insert("psk".to_string(), Value::from(password));
659 }
660 }
661 }
662
663 connection_settings.insert("802-11-wireless".to_string(), wifi_settings);
664 connection_settings.insert("802-11-wireless-security".to_string(), security_settings);
665
666 eprintln!("[network-manager] connection_settings: {:#?}", connection_settings);
669
670 let nm_proxy = zbus::blocking::Proxy::new(
672 &self.connection,
673 "org.freedesktop.NetworkManager",
674 "/org/freedesktop/NetworkManager",
675 "org.freedesktop.NetworkManager",
676 )?;
677
678 let call_result: zbus::Result<(zbus::zvariant::OwnedObjectPath, zbus::zvariant::OwnedObjectPath)> = nm_proxy.call("AddAndActivateConnection", &(connection_settings, "/", "/"));
680
681 match call_result {
682 Ok((conn_path, active_path)) => {
683 eprintln!(
684 "[network-manager] AddAndActivateConnection succeeded for ssid='{}' conn='{}' active='{}'",
685 config.ssid,
686 conn_path.as_str(),
687 active_path.as_str()
688 );
689 }
690 Err(e) => {
691 eprintln!(
692 "[network-manager] AddAndActivateConnection failed for ssid='{}': {:?}",
693 config.ssid,
694 e
695 );
696 return Err(e.into());
697 }
698 }
699
700 eprintln!("[network-manager] connect_to_wifi finished for ssid='{}'", config.ssid);
701
702 Ok(())
703 }
704
705 pub fn toggle_network_state(&self, enabled: bool) -> Result<bool> {
707 let nm_proxy = zbus::blocking::Proxy::new(
708 &self.connection,
709 "org.freedesktop.NetworkManager",
710 "/org/freedesktop/NetworkManager",
711 "org.freedesktop.NetworkManager",
712 )?;
713
714 let state = if enabled { "on" } else { "off" };
715 let _output = Command::new("nmcli")
716 .arg("networking")
717 .arg(state)
718 .output()?;
719
720 let current_state: bool = nm_proxy.get_property("NetworkingEnabled")?;
721 Ok(current_state)
722 }
723
724 pub fn get_wireless_enabled(&self) -> Result<bool> {
726 let nm_proxy = zbus::blocking::Proxy::new(
727 &self.connection,
728 "org.freedesktop.NetworkManager",
729 "/org/freedesktop/NetworkManager",
730 "org.freedesktop.NetworkManager",
731 )?;
732 Ok(nm_proxy.get_property("WirelessEnabled")?)
733 }
734
735 pub fn set_wireless_enabled(&self, enabled: bool) -> Result<()> {
737 let nm_proxy = zbus::blocking::Proxy::new(
738 &self.connection,
739 "org.freedesktop.NetworkManager",
740 "/org/freedesktop/NetworkManager",
741 "org.freedesktop.NetworkManager",
742 )?;
743 nm_proxy.set_property("WirelessEnabled", enabled)?;
744 Ok(())
745 }
746
747 pub fn is_wireless_available(&self) -> Result<bool> {
749 let devices_variant = self.proxy.get(
751 InterfaceName::from_static_str_unchecked("org.freedesktop.NetworkManager"),
752 "Devices",
753 )?;
754
755 if let Some(zbus::zvariant::Value::Array(devices)) = devices_variant.downcast_ref() {
756 let device_values = devices.get();
757 for device in device_values {
758 if let zbus::zvariant::Value::ObjectPath(ref device_path) = device {
759 let device_props = zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
760 .destination("org.freedesktop.NetworkManager")?
761 .path(device_path)?
762 .build()?;
763
764 let device_type_variant = device_props.get(
765 InterfaceName::from_static_str_unchecked("org.freedesktop.NetworkManager.Device"),
766 "DeviceType",
767 )?;
768
769 if let Some(zbus::zvariant::Value::U32(device_type)) = device_type_variant.downcast_ref() {
770 if device_type == &2u32 { return Ok(true);
772 }
773 }
774 }
775 }
776 }
777 Ok(false)
778 }
779
780 pub fn listen_network_changes(&self) -> Result<mpsc::Receiver<NetworkInfo>> {
782 let (tx, rx) = mpsc::channel();
783 let connection_clone = self.connection.clone();
784 let app_handle = self.app.clone();
785
786 std::thread::spawn(move || {
788 match zbus::blocking::Connection::system() {
789 Ok(conn) => {
790 if let Ok(proxy) = zbus::blocking::Proxy::new(
792 &conn,
793 "org.freedesktop.NetworkManager",
794 "/org/freedesktop/NetworkManager",
795 "org.freedesktop.NetworkManager",
796 ) {
797 if let Ok(mut signal) = proxy.receive_signal("StateChanged") {
798 while let Some(_msg) = signal.next() {
799 let network_manager = VSKNetworkManager {
800 connection: connection_clone.clone(),
801 proxy: zbus::blocking::fdo::PropertiesProxy::builder(
802 &connection_clone,
803 )
804 .destination("org.freedesktop.NetworkManager")
805 .unwrap()
806 .path("/org/freedesktop/NetworkManager")
807 .unwrap()
808 .build()
809 .unwrap(),
810 app: app_handle.clone(),
811 };
812
813 if let Ok(network_info) =
814 network_manager.get_current_network_state()
815 {
816 if tx.send(network_info).is_err() {
817 break;
818 }
819 }
820 }
821 }
822 }
823 }
824 Err(e) => {
825 eprintln!(
826 "Error al conectar con D-Bus para escuchar cambios de red: {:?}",
827 e
828 );
829 }
830 }
831 });
832
833 Ok(rx)
834 }
835
836 pub async fn disconnect_from_wifi(&self) -> Result<()> {
838 let _current_state = self.get_current_network_state()?;
840
841 let nm_proxy = zbus::blocking::Proxy::new(
843 &self.connection,
844 "org.freedesktop.NetworkManager",
845 "/org/freedesktop/NetworkManager",
846 "org.freedesktop.NetworkManager",
847 )?;
848
849 let active_connections_variant: zbus::zvariant::OwnedValue = self.proxy.get(
851 InterfaceName::from_static_str_unchecked("org.freedesktop.NetworkManager"),
852 "ActiveConnections",
853 )?;
854
855 let active_connections = match active_connections_variant.downcast_ref() {
857 Some(zbus::zvariant::Value::Array(arr)) => arr
858 .iter()
859 .filter_map(|v| match v {
860 zbus::zvariant::Value::ObjectPath(path) => {
861 Some(zbus::zvariant::OwnedObjectPath::from(path.to_owned()))
862 }
863 _ => None,
864 })
865 .collect::<Vec<zbus::zvariant::OwnedObjectPath>>(),
866 _ => Vec::new(),
867 };
868
869 if !active_connections.is_empty() {
870 nm_proxy.call::<_, _, ()>("DeactivateConnection", &(active_connections[0].as_str()))?;
871 Ok(())
872 } else {
873 Ok(())
874 }
875 }
876
877 pub fn get_saved_wifi_networks(&self) -> Result<Vec<NetworkInfo>> {
879 let settings_proxy = zbus::blocking::Proxy::new(
881 &self.connection,
882 "org.freedesktop.NetworkManager",
883 "/org/freedesktop/NetworkManager/Settings",
884 "org.freedesktop.NetworkManager.Settings",
885 )?;
886
887 let connections: Vec<zbus::zvariant::OwnedObjectPath> =
889 settings_proxy.call("ListConnections", &())?;
890 let mut saved_networks = Vec::new();
891
892 for conn_path in connections {
894 let conn_proxy = zbus::blocking::Proxy::new(
896 &self.connection,
897 "org.freedesktop.NetworkManager",
898 conn_path.as_str(),
899 "org.freedesktop.NetworkManager.Settings.Connection",
900 )?;
901
902 let settings: std::collections::HashMap<String, zbus::zvariant::OwnedValue> =
904 conn_proxy.call("GetSettings", &())?;
905
906 if let Some(connection) = settings.get("connection") {
908 let connection_value = connection.to_owned();
909 let connection_dict =
910 match <zbus::zvariant::Value<'_> as Clone>::clone(&connection_value)
911 .downcast::<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>(
912 ) {
913 Some(dict) => dict,
914 _ => continue,
915 };
916
917 if let Some(conn_type) = connection_dict.get("type") {
919 let conn_type_value = conn_type.to_owned();
920 let conn_type_str =
921 match <zbus::zvariant::Value<'_> as Clone>::clone(&conn_type_value)
922 .downcast::<String>()
923 {
924 Some(s) => s,
925 _ => continue,
926 };
927
928 if conn_type_str == "802-11-wireless" {
930 let mut network_info = NetworkInfo::default();
931 network_info.connection_type = "wifi".to_string();
932
933 if let Some(id) = connection_dict.get("id") {
935 let id_value = id.to_owned();
936 if let Some(name) =
937 <zbus::zvariant::Value<'_> as Clone>::clone(&id_value)
938 .downcast::<String>()
939 {
940 network_info.name = name;
941 }
942 }
943
944 if let Some(wireless) = settings.get("802-11-wireless") {
946 let wireless_value = wireless.to_owned();
947 let wireless_dict = match <zbus::zvariant::Value<'_> as Clone>::clone(&wireless_value).downcast::<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>() {
948 Some(dict) => dict,
949 _ => continue,
950 };
951
952 if let Some(ssid) = wireless_dict.get("ssid") {
953 let ssid_value = ssid.to_owned();
954 if let Some(ssid_bytes) =
955 <zbus::zvariant::Value<'_> as Clone>::clone(&ssid_value)
956 .downcast::<Vec<u8>>()
957 {
958 if let Ok(ssid_str) = String::from_utf8(ssid_bytes) {
959 network_info.ssid = ssid_str;
960 }
961 }
962 }
963 }
964
965 if let Some(security) = settings.get("802-11-wireless-security") {
967 let security_value = security.to_owned();
968 let security_dict = match <zbus::zvariant::Value<'_> as Clone>::clone(&security_value).downcast::<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>() {
969 Some(dict) => dict,
970 _ => {
971 network_info.security_type = WiFiSecurityType::None;
972 saved_networks.push(network_info);
973 continue;
974 },
975 };
976
977 if let Some(key_mgmt) = security_dict.get("key-mgmt") {
978 let key_mgmt_value = key_mgmt.to_owned();
979 if let Some(key_mgmt_str) =
980 <zbus::zvariant::Value<'_> as Clone>::clone(&key_mgmt_value)
981 .downcast::<String>()
982 {
983 match key_mgmt_str.as_str() {
984 "none" => {
985 network_info.security_type = WiFiSecurityType::None
986 }
987 "wpa-psk" => {
988 network_info.security_type = WiFiSecurityType::WpaPsk
989 }
990 "wpa-eap" => {
991 network_info.security_type = WiFiSecurityType::WpaEap
992 }
993 _ => network_info.security_type = WiFiSecurityType::None,
994 }
995 }
996 }
997 } else {
998 network_info.security_type = WiFiSecurityType::None;
999 }
1000
1001 saved_networks.push(network_info);
1003 }
1004 }
1005 }
1006 }
1007
1008 Ok(saved_networks)
1009 }
1010
1011 pub fn delete_wifi_connection(&self, ssid: &str) -> Result<bool> {
1013 let settings_proxy = zbus::blocking::Proxy::new(
1015 &self.connection,
1016 "org.freedesktop.NetworkManager",
1017 "/org/freedesktop/NetworkManager/Settings",
1018 "org.freedesktop.NetworkManager.Settings",
1019 )?;
1020
1021 let connections: Vec<zbus::zvariant::OwnedObjectPath> =
1023 settings_proxy.call("ListConnections", &())?;
1024
1025 for conn_path in connections {
1027 let conn_proxy = zbus::blocking::Proxy::new(
1029 &self.connection,
1030 "org.freedesktop.NetworkManager",
1031 conn_path.as_str(),
1032 "org.freedesktop.NetworkManager.Settings.Connection",
1033 )?;
1034
1035 let settings: std::collections::HashMap<String, zbus::zvariant::OwnedValue> =
1037 conn_proxy.call("GetSettings", &())?;
1038
1039 if let Some(connection) = settings.get("connection") {
1041 let connection_value = connection.to_owned();
1042 let connection_dict =
1043 match <zbus::zvariant::Value<'_> as Clone>::clone(&connection_value)
1044 .downcast::<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>(
1045 ) {
1046 Some(dict) => dict,
1047 _ => continue,
1048 };
1049
1050 if let Some(conn_type) = connection_dict.get("type") {
1052 let conn_type_value = conn_type.to_owned();
1053 let conn_type_str =
1054 match <zbus::zvariant::Value<'_> as Clone>::clone(&conn_type_value)
1055 .downcast::<String>()
1056 {
1057 Some(s) => s,
1058 _ => continue,
1059 };
1060
1061 if conn_type_str == "802-11-wireless" {
1063 if let Some(wireless) = settings.get("802-11-wireless") {
1064 let wireless_value = wireless.to_owned();
1065 let wireless_dict = match <zbus::zvariant::Value<'_> as Clone>::clone(&wireless_value).downcast::<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>() {
1066 Some(dict) => dict,
1067 _ => continue,
1068 };
1069
1070 if let Some(ssid_value) = wireless_dict.get("ssid") {
1071 let ssid_owned = ssid_value.to_owned();
1072 if let Some(ssid_bytes) =
1073 <zbus::zvariant::Value<'_> as Clone>::clone(&ssid_owned)
1074 .downcast::<Vec<u8>>()
1075 {
1076 if let Ok(conn_ssid_str) = String::from_utf8(ssid_bytes) {
1077 if conn_ssid_str == ssid {
1079 conn_proxy.call::<_, _, ()>("Delete", &())?;
1080 return Ok(true);
1081 }
1082 }
1083 }
1084 }
1085 }
1086 }
1087 }
1088 }
1089 }
1090
1091 Ok(false)
1093 }
1094}
1095
1096pub async fn init(
1098 app: &AppHandle<tauri::Wry>,
1099 _api: PluginApi<tauri::Wry, ()>,
1100) -> Result<VSKNetworkManager<'static, tauri::Wry>> {
1101 Ok(VSKNetworkManager::new(app.clone()).await?)
1102}