tauri_plugin_network_manager/
desktop.rs

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    /// Get WiFi icon based on signal strength
13    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    /// Create a new VSKNetworkManager instance
32    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        // Get active connections
58        let active_connections_variant = self.proxy.get(
59            InterfaceName::from_static_str_unchecked("org.freedesktop.NetworkManager"),
60            "ActiveConnections",
61        )?;
62
63        // If no active connections, return default
64        match active_connections_variant.downcast_ref() {
65            Some(Value::Array(arr)) if !arr.is_empty() => {
66                // Get the first active connection path
67                match arr[0] {
68                    zbus::zvariant::Value::ObjectPath(ref path) => {
69                        // Get devices for this connection
70                        // Crear un proxy de propiedades para obtener las propiedades
71                        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                        // Get the first device (if available)
85                        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                        // Retrieve connection details
98                        // Crear un proxy de propiedades para el dispositivo
99                        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, // 2 = ACTIVATED
121                            _ => false,
122                        };
123
124                        // Determine connection type
125                        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                        // Default network info
135                        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                        // For WiFi networks, get additional details
160                        if connection_type_str == "WiFi" {
161                            // Get active access point
162                            // Crear un proxy de propiedades para el dispositivo inalámbrico
163                            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                                // Get SSID
187                                // Crear un proxy de propiedades para el punto de acceso
188                                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                                        // Convertir el array de bytes a una cadena UTF-8
204                                        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                                // Get signal strength
222                                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                                // Update icon based on signal strength
236                                network_info.icon =
237                                    Self::get_wifi_icon(network_info.signal_strength);
238
239                                // Determine security type
240                                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                                // Obtener key-mgmt si está disponible
264                                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                                        // Fallback a flags
281                                        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                                    // Fallback a flags
299                                    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                                // Asignar el security_type calculado a network_info
317                                network_info.security_type = security_type;
318
319                                // Elimino el bloque duplicado que intenta crear y agregar network_info fuera del contexto correcto
320                                // Este bloque no pertenece aquí y causa errores de compilación
321                            }
322                        } else {
323                            // This is a wired connection
324                            network_info.icon = Self::get_wired_icon(network_info.is_connected);
325                        }
326                        // Get IP configuration
327                        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                        // Retrieve IP address if available
335                        if let Some(zbus::zvariant::Value::ObjectPath(config_path)) =
336                            ip4_config_path.downcast_ref()
337                        {
338                            // Crear un proxy de propiedades para la configuración IP
339                            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    /// List available WiFi networks
375    pub fn list_wifi_networks(&self) -> Result<Vec<NetworkInfo>> {
376        // Get all devices
377        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            // Iterate over devices in the array
387            let device_values = devices.get();
388            for device in device_values {
389                if let zbus::zvariant::Value::ObjectPath(ref device_path) = device {
390                    // Create a device proxy
391                    let device_props =
392                        zbus::blocking::fdo::PropertiesProxy::builder(&self.connection)
393                            .destination("org.freedesktop.NetworkManager")?
394                            .path(device_path)?
395                            .build()?;
396
397                    // Check if this is a wireless device
398                    let device_type_variant = device_props.get(
399                        InterfaceName::from_static_str_unchecked(
400                            "org.freedesktop.NetworkManager.Device",
401                        ),
402                        "DeviceType",
403                    )?;
404
405                    // DeviceType 2 is WiFi
406                    if let Some(zbus::zvariant::Value::U32(device_type)) =
407                        device_type_variant.downcast_ref()
408                    {
409                        if device_type == &2u32 {
410                            // This is a WiFi device, get its access points
411                            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                                // Iterate over access points
428                                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                                        // Obtener SSID
439                                        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                                                // Convertir el array de bytes a una cadena UTF-8
449                                                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                                        // Obtener fuerza de señal
466                                        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                                        // Obtener flags
479                                        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                                        // Obtener key-mgmt si está disponible
503                                        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                                                // Fallback a flags
520                                                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                                            // Fallback a flags
538                                            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        // Sort networks by signal strength (descending)
596        networks.sort_by(|a, b| b.signal_strength.cmp(&a.signal_strength));
597
598        Ok(networks)
599    }
600
601    /// Connect to a WiFi network
602    pub async fn connect_to_wifi(&self, config: WiFiConnectionConfig) -> Result<()> {
603        // Trace: start
604        eprintln!("[network-manager] connect_to_wifi called: ssid='{}' security={:?} username={:?}",
605                  config.ssid, config.security_type, config.username);
606
607        // Create connection settings
608        let mut connection_settings = HashMap::new();
609        let mut wifi_settings = HashMap::new();
610        let mut security_settings = HashMap::new();
611
612        // Set connection name and type
613        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        // Set WiFi settings
619        wifi_settings.insert("ssid".to_string(), Value::from(config.ssid.clone()));
620        wifi_settings.insert("mode".to_string(), Value::from("infrastructure"));
621
622        // Set security settings based on security type
623        match config.security_type {
624            WiFiSecurityType::None => {
625                // No security settings needed
626            }
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        // Log constructed settings for debugging
667        // Note: Value implements Debug via zvariant
668        eprintln!("[network-manager] connection_settings: {:#?}", connection_settings);
669
670        // Crear un proxy para NetworkManager
671        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        // Llamar al método AddAndActivateConnection (trace result)
679        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    /// Toggle network state
706    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    /// Get wireless enabled state
725    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    /// Set wireless enabled state
736    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    /// Check if wireless device is available
748    pub fn is_wireless_available(&self) -> Result<bool> {
749         // Get all devices
750        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 { // 2 = WiFi
771                            return Ok(true);
772                        }
773                    }
774                }
775            }
776        }
777        Ok(false)
778    }
779
780    /// Listen for network changes
781    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        // Crear un hilo para escuchar los cambios de red
787        std::thread::spawn(move || {
788            match zbus::blocking::Connection::system() {
789                Ok(conn) => {
790                    // Proxy para el objeto raíz, interfaz DBus.Properties
791                    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    /// Disconnect from the current WiFi network
837    pub async fn disconnect_from_wifi(&self) -> Result<()> {
838        // Obtener el estado actual de la red para identificar la conexión activa
839        let _current_state = self.get_current_network_state()?;
840
841        // Crear un proxy para NetworkManager
842        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        // Obtener las conexiones activas
850        let active_connections_variant: zbus::zvariant::OwnedValue = self.proxy.get(
851            InterfaceName::from_static_str_unchecked("org.freedesktop.NetworkManager"),
852            "ActiveConnections",
853        )?;
854
855        // Convertir el valor a un vector de ObjectPath
856        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    /// Get the list of saved WiFi networks
878    pub fn get_saved_wifi_networks(&self) -> Result<Vec<NetworkInfo>> {
879        // Crear un proxy para el servicio de configuración de NetworkManager
880        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        // Obtener todas las conexiones guardadas
888        let connections: Vec<zbus::zvariant::OwnedObjectPath> =
889            settings_proxy.call("ListConnections", &())?;
890        let mut saved_networks = Vec::new();
891
892        // Procesar cada conexión guardada
893        for conn_path in connections {
894            // Crear un proxy para cada conexión
895            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            // Obtener la configuración de la conexión como un HashMap
903            let settings: std::collections::HashMap<String, zbus::zvariant::OwnedValue> =
904                conn_proxy.call("GetSettings", &())?;
905
906            // Verificar si es una conexión WiFi
907            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                // Verificar el tipo de conexión
918                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                    // Si es una conexión WiFi, extraer la información
929                    if conn_type_str == "802-11-wireless" {
930                        let mut network_info = NetworkInfo::default();
931                        network_info.connection_type = "wifi".to_string();
932
933                        // Obtener el nombre de la conexión
934                        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                        // Obtener el SSID
945                        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                        // Determinar el tipo de seguridad
966                        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                        // Agregar a la lista de redes guardadas
1002                        saved_networks.push(network_info);
1003                    }
1004                }
1005            }
1006        }
1007
1008        Ok(saved_networks)
1009    }
1010
1011    /// Delete a saved WiFi connection by SSID
1012    pub fn delete_wifi_connection(&self, ssid: &str) -> Result<bool> {
1013        // Crear un proxy para el servicio de configuración de NetworkManager
1014        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        // Obtener todas las conexiones guardadas
1022        let connections: Vec<zbus::zvariant::OwnedObjectPath> =
1023            settings_proxy.call("ListConnections", &())?;
1024
1025        // Procesar cada conexión guardada
1026        for conn_path in connections {
1027            // Crear un proxy para cada conexión
1028            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            // Obtener la configuración de la conexión como un HashMap
1036            let settings: std::collections::HashMap<String, zbus::zvariant::OwnedValue> =
1037                conn_proxy.call("GetSettings", &())?;
1038
1039            // Verificar si es una conexión WiFi
1040            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                // Verificar el tipo de conexión
1051                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                    // Si es una conexión WiFi, verificar el SSID
1062                    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                                        // Si el SSID coincide, eliminar la conexión
1078                                        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        // No se encontró ninguna conexión con el SSID especificado
1092        Ok(false)
1093    }
1094}
1095
1096/// Initialize the network manager plugin
1097pub 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}