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;
|
self.new_connection = None;
|
||||||
}
|
}
|
||||||
}
|
} else if let NetworkManagerRequest::Password(ssid, _) = &req {
|
||||||
if let NetworkManagerRequest::Password(ssid, _) = &req {
|
|
||||||
if let Some(
|
if let Some(
|
||||||
NewConnectionState::EnterPassword { access_point, .. }
|
NewConnectionState::EnterPassword { access_point, .. }
|
||||||
| NewConnectionState::Waiting(access_point),
|
| NewConnectionState::Waiting(access_point),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use cosmic_dbus_networkmanager::{device::wireless::WirelessDevice, interface::en
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use zbus::zvariant::ObjectPath;
|
||||||
|
|
||||||
pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result<Vec<AccessPoint>> {
|
pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result<Vec<AccessPoint>> {
|
||||||
device.request_scan(HashMap::new()).await?;
|
device.request_scan(HashMap::new()).await?;
|
||||||
|
|
@ -40,6 +41,7 @@ pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result<
|
||||||
strength,
|
strength,
|
||||||
state,
|
state,
|
||||||
working: false,
|
working: false,
|
||||||
|
path: ap.path().to_owned(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -56,4 +58,5 @@ pub struct AccessPoint {
|
||||||
pub strength: u8,
|
pub strength: u8,
|
||||||
pub state: DeviceState,
|
pub state: DeviceState,
|
||||||
pub working: bool,
|
pub working: bool,
|
||||||
|
pub path: ObjectPath<'static>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use futures::{
|
||||||
};
|
};
|
||||||
use tokio::{process::Command, time::timeout};
|
use tokio::{process::Command, time::timeout};
|
||||||
use zbus::{
|
use zbus::{
|
||||||
zvariant::{self, ObjectPath, Value},
|
zvariant::{self, ObjectPath, OwnedObjectPath, Value},
|
||||||
Connection,
|
Connection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -167,205 +167,18 @@ async fn start_listening(
|
||||||
_ = output.send(response).await;
|
_ = output.send(response).await;
|
||||||
}
|
}
|
||||||
Some(NetworkManagerRequest::Password(ssid, password)) => {
|
Some(NetworkManagerRequest::Password(ssid, password)) => {
|
||||||
let s = match NetworkManagerSettings::new(&conn).await {
|
// Create a connection
|
||||||
Ok(s) => s,
|
let nm_state = NetworkManagerState::new(&conn).await.unwrap_or_default();
|
||||||
Err(_) => return State::Finished,
|
let success = nm_state
|
||||||
};
|
.connect_wifi(&conn, &ssid, Some(&password))
|
||||||
|
.await
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
let mut status: Option<NetworkManagerEvent> = None;
|
let status = Some(NetworkManagerEvent::RequestResponse {
|
||||||
|
req: NetworkManagerRequest::Password(ssid.clone(), password.clone()),
|
||||||
// First try known connections
|
success,
|
||||||
// TODO more convenient methods of managing settings
|
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(e) = status {
|
if let Some(e) = status {
|
||||||
_ = output.send(e).await;
|
_ = output.send(e).await;
|
||||||
|
|
@ -452,10 +265,14 @@ async fn start_listening(
|
||||||
|
|
||||||
return State::Waiting(conn, rx);
|
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
|
_ = output
|
||||||
.send(NetworkManagerEvent::RequestResponse {
|
.send(NetworkManagerEvent::RequestResponse {
|
||||||
req: NetworkManagerRequest::SelectAccessPoint(ssid.clone()),
|
req: NetworkManagerRequest::SelectAccessPoint(ssid.clone()),
|
||||||
success: false,
|
success,
|
||||||
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -619,4 +436,138 @@ impl NetworkManagerState {
|
||||||
self.known_access_points = Vec::new();
|
self.known_access_points = Vec::new();
|
||||||
self.wireless_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