fix(network): add connection if it doesn't exist when it is selected
This commit is contained in:
parent
8900b93c85
commit
db47edf9b6
3 changed files with 155 additions and 202 deletions
|
|
@ -293,8 +293,7 @@ impl cosmic::Application for CosmicNetworkApplet {
|
|||
{
|
||||
self.new_connection = None;
|
||||
}
|
||||
}
|
||||
if let NetworkManagerRequest::Password(ssid, _) = &req {
|
||||
} else if let NetworkManagerRequest::Password(ssid, _) = &req {
|
||||
if let Some(
|
||||
NewConnectionState::EnterPassword { access_point, .. }
|
||||
| NewConnectionState::Waiting(access_point),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use cosmic_dbus_networkmanager::{device::wireless::WirelessDevice, interface::en
|
|||
use futures_util::StreamExt;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashMap;
|
||||
use zbus::zvariant::ObjectPath;
|
||||
|
||||
pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result<Vec<AccessPoint>> {
|
||||
device.request_scan(HashMap::new()).await?;
|
||||
|
|
@ -40,6 +41,7 @@ pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result<
|
|||
strength,
|
||||
state,
|
||||
working: false,
|
||||
path: ap.path().to_owned(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -56,4 +58,5 @@ pub struct AccessPoint {
|
|||
pub strength: u8,
|
||||
pub state: DeviceState,
|
||||
pub working: bool,
|
||||
pub path: ObjectPath<'static>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use futures::{
|
|||
};
|
||||
use tokio::{process::Command, time::timeout};
|
||||
use zbus::{
|
||||
zvariant::{self, ObjectPath, Value},
|
||||
zvariant::{self, ObjectPath, OwnedObjectPath, Value},
|
||||
Connection,
|
||||
};
|
||||
|
||||
|
|
@ -167,205 +167,18 @@ async fn start_listening(
|
|||
_ = output.send(response).await;
|
||||
}
|
||||
Some(NetworkManagerRequest::Password(ssid, password)) => {
|
||||
let s = match NetworkManagerSettings::new(&conn).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => return State::Finished,
|
||||
};
|
||||
// Create a connection
|
||||
let nm_state = NetworkManagerState::new(&conn).await.unwrap_or_default();
|
||||
let success = nm_state
|
||||
.connect_wifi(&conn, &ssid, Some(&password))
|
||||
.await
|
||||
.is_ok();
|
||||
|
||||
let mut status: Option<NetworkManagerEvent> = None;
|
||||
|
||||
// First try known connections
|
||||
// TODO more convenient methods of managing settings
|
||||
for c in s.list_connections().await.unwrap_or_default() {
|
||||
let mut settings = match c.get_settings().await.ok() {
|
||||
Some(s) => s,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let cur_ssid = settings
|
||||
.get("802-11-wireless")
|
||||
.and_then(|w| w.get("ssid"))
|
||||
.cloned()
|
||||
.and_then(|ssid| ssid.try_into().ok())
|
||||
.and_then(|ssid| String::from_utf8(ssid).ok());
|
||||
if cur_ssid.as_ref() != Some(&ssid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut secrets = match c.get_secrets("802-11-wireless-security").await {
|
||||
Ok(s) => s,
|
||||
_ => HashMap::from([(
|
||||
"802-11-wireless-security".into(),
|
||||
HashMap::from([
|
||||
(
|
||||
"psk".into(),
|
||||
Value::Str(password.as_str().into()).to_owned(),
|
||||
),
|
||||
("key-mgmt".into(), Value::Str("wpa-psk".into()).to_owned()),
|
||||
]),
|
||||
)]),
|
||||
};
|
||||
if let Some(s) = secrets.get_mut("802-11-wireless-security") {
|
||||
s.insert("psk".into(), Value::Str(password.clone().into()).to_owned());
|
||||
settings.extend(secrets.into_iter());
|
||||
let settings: HashMap<_, _> = settings
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let map = (
|
||||
k.as_str(),
|
||||
v.iter()
|
||||
.map(|(k, v)| (k.as_str(), v.into()))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
);
|
||||
map
|
||||
})
|
||||
.collect();
|
||||
let updated = c.update(settings).await;
|
||||
if updated.is_ok() {
|
||||
let success = if let Ok(path) = network_manager
|
||||
.deref()
|
||||
.activate_connection(
|
||||
c.deref().path(),
|
||||
&ObjectPath::try_from("/").unwrap(),
|
||||
&ObjectPath::try_from("/").unwrap(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
// let active_conn = ActiveConnection::from(ActiveConnectionProxy::from(conn.1));
|
||||
let dummy = ActiveConnectionProxy::new(&conn).await.unwrap();
|
||||
let active = ActiveConnectionProxy::builder(&conn)
|
||||
.path(path)
|
||||
.unwrap()
|
||||
.destination(dummy.destination())
|
||||
.unwrap()
|
||||
.interface(dummy.interface())
|
||||
.unwrap()
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
let mut state = enums::ActiveConnectionState::from(
|
||||
active.state().await.unwrap_or_default(),
|
||||
);
|
||||
|
||||
while matches!(state, enums::ActiveConnectionState::Activating)
|
||||
{
|
||||
if let Ok(Some(s)) = timeout(
|
||||
Duration::from_secs(10),
|
||||
active.receive_state_changed().await.next(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
state = s.get().await.unwrap_or_default().into();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
matches!(state, enums::ActiveConnectionState::Activated)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
status = Some(NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::Password(
|
||||
ssid.clone(),
|
||||
password.clone(),
|
||||
),
|
||||
success,
|
||||
state: NetworkManagerState::new(&conn)
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// create a connection
|
||||
if status.is_none() {
|
||||
for device in network_manager.devices().await.ok().unwrap_or_default() {
|
||||
if matches!(
|
||||
device.device_type().await.unwrap_or(DeviceType::Other),
|
||||
DeviceType::Wifi
|
||||
) {
|
||||
let conn_settings: HashMap<&str, HashMap<&str, zvariant::Value>> =
|
||||
HashMap::from([
|
||||
(
|
||||
"802-11-wireless",
|
||||
HashMap::from([(
|
||||
"ssid",
|
||||
Value::Array(ssid.as_bytes().into()),
|
||||
)]),
|
||||
),
|
||||
(
|
||||
"connection",
|
||||
HashMap::from([
|
||||
("id", Value::Str(ssid.as_str().into())),
|
||||
("type", Value::Str("802-11-wireless".into())),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"802-11-wireless-security",
|
||||
HashMap::from([
|
||||
("psk", Value::Str(password.as_str().into())),
|
||||
("key-mgmt", Value::Str("wpa-psk".into())),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
let success = if let Ok((_, path)) = network_manager
|
||||
.add_and_activate_connection(
|
||||
conn_settings,
|
||||
device.path(),
|
||||
&ObjectPath::try_from("/").unwrap(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
let dummy = ActiveConnectionProxy::new(&conn).await.unwrap();
|
||||
let active = ActiveConnectionProxy::builder(&conn)
|
||||
.path(path)
|
||||
.unwrap()
|
||||
.destination(dummy.destination())
|
||||
.unwrap()
|
||||
.interface(dummy.interface())
|
||||
.unwrap()
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
let mut state = enums::ActiveConnectionState::from(
|
||||
active.state().await.unwrap_or_default(),
|
||||
);
|
||||
while matches!(state, enums::ActiveConnectionState::Activating)
|
||||
{
|
||||
if let Ok(Some(s)) = timeout(
|
||||
Duration::from_secs(10),
|
||||
active.receive_state_changed().await.next(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
state = s.get().await.unwrap_or_default().into();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
matches!(state, enums::ActiveConnectionState::Activated)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
status = Some(NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::Password(
|
||||
ssid.clone(),
|
||||
password.clone(),
|
||||
),
|
||||
success,
|
||||
state: NetworkManagerState::new(&conn)
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let status = Some(NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::Password(ssid.clone(), password.clone()),
|
||||
success,
|
||||
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||
});
|
||||
|
||||
if let Some(e) = status {
|
||||
_ = output.send(e).await;
|
||||
|
|
@ -452,10 +265,14 @@ async fn start_listening(
|
|||
|
||||
return State::Waiting(conn, rx);
|
||||
}
|
||||
|
||||
let state = NetworkManagerState::new(&conn).await.unwrap_or_default();
|
||||
let success = state.connect_wifi(&conn, &ssid, None).await.is_ok();
|
||||
|
||||
_ = output
|
||||
.send(NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::SelectAccessPoint(ssid.clone()),
|
||||
success: false,
|
||||
success,
|
||||
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||
})
|
||||
.await;
|
||||
|
|
@ -619,4 +436,138 @@ impl NetworkManagerState {
|
|||
self.known_access_points = Vec::new();
|
||||
self.wireless_access_points = Vec::new();
|
||||
}
|
||||
|
||||
async fn connect_wifi<'a>(
|
||||
&self,
|
||||
conn: &Connection,
|
||||
ssid: &str,
|
||||
password: Option<&str>,
|
||||
) -> anyhow::Result<()> {
|
||||
let nm = NetworkManager::new(conn).await?;
|
||||
|
||||
for c in nm.active_connections().await.unwrap_or_default() {
|
||||
if self
|
||||
.wireless_access_points
|
||||
.iter()
|
||||
.any(|w| Ok(Some(w.ssid.clone())) == c.cached_id())
|
||||
{
|
||||
_ = nm.deactivate_connection(&c).await;
|
||||
}
|
||||
}
|
||||
|
||||
let Some(ap) = self
|
||||
.wireless_access_points
|
||||
.iter()
|
||||
.find(|ap| ap.ssid == ssid)
|
||||
else {
|
||||
return Err(anyhow::anyhow!("Access point not found"));
|
||||
};
|
||||
|
||||
let mut conn_settings: HashMap<&str, HashMap<&str, zvariant::Value>> = HashMap::from([
|
||||
(
|
||||
"802-11-wireless",
|
||||
HashMap::from([("ssid", Value::Array(ssid.as_bytes().into()))]),
|
||||
),
|
||||
(
|
||||
"connection",
|
||||
HashMap::from([
|
||||
("id", Value::Str(ssid.into())),
|
||||
("type", Value::Str("802-11-wireless".into())),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
|
||||
if let Some(pass) = password {
|
||||
conn_settings.insert(
|
||||
"802-11-wireless-security",
|
||||
HashMap::from([
|
||||
("psk", Value::Str(pass.into())),
|
||||
("key-mgmt", Value::Str("wpa-psk".into())),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
let devices = nm.devices().await?;
|
||||
for device in devices {
|
||||
if !matches!(
|
||||
device.device_type().await.unwrap_or(DeviceType::Other),
|
||||
DeviceType::Wifi
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// first check if the conn exists
|
||||
let s = NetworkManagerSettings::new(conn).await?;
|
||||
let known_conns = s.list_connections().await.unwrap_or_default();
|
||||
let mut known_conn = None;
|
||||
for c in known_conns {
|
||||
let settings = c.get_settings().await.ok().unwrap_or_default();
|
||||
let s = Settings::new(settings);
|
||||
if let Some(cur_ssid) = s
|
||||
.wifi
|
||||
.clone()
|
||||
.and_then(|w| w.ssid)
|
||||
.and_then(|ssid| String::from_utf8(ssid).ok())
|
||||
{
|
||||
if cur_ssid == ssid {
|
||||
known_conn = Some(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let active_conn = if let Some(known_conn) = known_conn.as_ref() {
|
||||
// update settings if needed
|
||||
let mut settings = known_conn.get_settings().await.ok().unwrap_or_default();
|
||||
settings.extend(conn_settings.iter().map(|s| {
|
||||
let map = (
|
||||
s.0.to_string(),
|
||||
s.1.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.clone().into()))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
);
|
||||
map
|
||||
}));
|
||||
|
||||
known_conn.update(conn_settings).await?;
|
||||
|
||||
nm.activate_connection(known_conn, &device).await?
|
||||
} else {
|
||||
nm.add_and_activate_connection(conn_settings, device.path(), &ap.path)
|
||||
.await?;
|
||||
let active_conns = nm.active_connections().await.unwrap_or_default();
|
||||
let Some(active_conn) = active_conns
|
||||
.into_iter()
|
||||
.find(|ac| ac.cached_id() == Ok(Some(ssid.to_string())))
|
||||
else {
|
||||
return Err(anyhow::anyhow!("No active connection found"));
|
||||
};
|
||||
active_conn
|
||||
};
|
||||
let mut state =
|
||||
enums::ActiveConnectionState::from(active_conn.state().await.unwrap_or_default());
|
||||
return match state {
|
||||
ActiveConnectionState::Activating => {
|
||||
if let Ok(Some(s)) = tokio::time::timeout(
|
||||
Duration::from_secs(20),
|
||||
active_conn.receive_state_changed().await.next(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
state = s.get().await.unwrap_or_default().into();
|
||||
if matches!(state, enums::ActiveConnectionState::Activated) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Failed to activate connection"))
|
||||
}
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Failed to activate connection"))
|
||||
}
|
||||
}
|
||||
ActiveConnectionState::Activated => Ok(()),
|
||||
_ => Err(anyhow::anyhow!("Failed to activate connection")),
|
||||
};
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!("No wifi device found"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue