@@ -11,6 +11,7 @@ use cosmic_settings_network_manager_subscription::{
1111use indexmap:: IndexMap ;
1212use rustc_hash:: FxHashSet ;
1313use secure_string:: SecureString ;
14+ use nmrs:: { ConnectionError , EapMethod , EapOptions , Phase2 , WifiSecurity } ;
1415use std:: {
1516 borrow:: Cow ,
1617 collections:: { BTreeMap , BTreeSet } ,
@@ -162,6 +163,7 @@ struct CosmicNetworkApplet {
162163 nm_task : Option < tokio:: sync:: oneshot:: Sender < ( ) > > ,
163164 secret_tx : Option < tokio:: sync:: mpsc:: Sender < nm_secret_agent:: Request > > ,
164165 nm_state : MyNetworkState ,
166+ nm : Option < nmrs:: NetworkManager > ,
165167
166168 // UI state
167169 show_visible_networks : bool ,
@@ -502,6 +504,7 @@ pub(crate) enum Message {
502504 ConnectVPNWithPassword ,
503505 VPNPasswordUpdate ( SecureString ) ,
504506 CancelVPNConnection ,
507+ NmrsReady ( Option < nmrs:: NetworkManager > ) ,
505508}
506509
507510#[ derive( Debug , Clone ) ]
@@ -841,56 +844,39 @@ impl cosmic::Application for CosmicNetworkApplet {
841844 }
842845 }
843846 Message :: SelectWirelessAccessPoint ( access_point) => {
844- let Some ( tx) = self . nm_sender . as_ref ( ) else {
845- return Task :: none ( ) ;
847+ let Some ( nm) = self . nm . clone ( ) else {
848+ return cosmic:: task:: message ( Message :: Error (
849+ "Network manager not initialized" . to_string ( )
850+ ) ) . map ( cosmic:: Action :: App ) ;
846851 } ;
847-
852+
853+ // Open networks - connect immediately
848854 if matches ! ( access_point. network_type, NetworkType :: Open ) {
849- if let Err ( err) =
850- tx. unbounded_send ( network_manager:: Request :: SelectAccessPoint (
851- access_point. ssid . clone ( ) ,
852- access_point. hw_address ,
853- access_point. network_type ,
854- self . secret_tx . clone ( ) ,
855- ) )
856- {
857- if err. is_disconnected ( ) {
858- return system_conn ( ) . map ( cosmic:: Action :: App ) ;
859- }
860-
861- tracing:: error!( "{err:?}" ) ;
862- }
855+ let ssid = access_point. ssid . to_string ( ) ;
863856 self . new_connection = Some ( NewConnectionState :: Waiting ( access_point) ) ;
864- } else {
865- if self
866- . nm_state
867- . nm_state
868- . known_access_points
869- . contains ( & access_point)
870- {
871- if let Err ( err) =
872- tx. unbounded_send ( network_manager:: Request :: SelectAccessPoint (
873- access_point. ssid . clone ( ) ,
874- access_point. hw_address ,
875- access_point. network_type ,
876- self . secret_tx . clone ( ) ,
877- ) )
878- {
879- if err. is_disconnected ( ) {
880- return system_conn ( ) . map ( cosmic:: Action :: App ) ;
857+
858+ return cosmic:: task:: future ( async move {
859+ match nm. connect ( & ssid, WifiSecurity :: Open ) . await {
860+ Ok ( ( ) ) => {
861+ tracing:: info!( "Connected to open network {}" , ssid) ;
862+ Message :: Refresh
863+ }
864+ Err ( e) => {
865+ tracing:: error!( "Failed to connect to {}: {}" , ssid, e) ;
866+ Message :: Error ( format ! ( "Failed to connect to '{}': {}" , ssid, e) )
881867 }
882-
883- tracing:: error!( "{err:?}" ) ;
884868 }
885- }
886- self . new_connection = Some ( NewConnectionState :: EnterPassword {
887- access_point,
888- description : None ,
889- identity : String :: new ( ) ,
890- password : String :: new ( ) . into ( ) ,
891- password_hidden : true ,
892- } ) ;
869+ } ) . map ( cosmic:: Action :: App ) ;
893870 }
871+
872+ // Secured networks - show password dialog
873+ self . new_connection = Some ( NewConnectionState :: EnterPassword {
874+ access_point,
875+ description : None ,
876+ identity : String :: new ( ) ,
877+ password : String :: new ( ) . into ( ) ,
878+ password_hidden : true ,
879+ } ) ;
894880 }
895881 Message :: ToggleVisibleNetworks => {
896882 self . new_connection = None ;
@@ -1046,35 +1032,73 @@ impl cosmic::Application for CosmicNetworkApplet {
10461032 }
10471033 }
10481034 Message :: ConnectWithPassword => {
1049- // save password
1050- let Some ( tx) = self . nm_sender . as_ref ( ) else {
1051- return Task :: none ( ) ;
1052- } ;
1053-
1054- if let Some ( NewConnectionState :: EnterPassword {
1035+ let Some ( NewConnectionState :: EnterPassword {
10551036 password,
10561037 access_point,
10571038 identity,
10581039 ..
1059- } ) = self . new_connection . take ( )
1060- {
1061- let is_enterprise: bool = matches ! ( access_point. network_type, NetworkType :: EAP ) ;
1062-
1063- if let Err ( err) = tx. unbounded_send ( network_manager:: Request :: Authenticate {
1064- ssid : access_point. ssid . to_string ( ) ,
1065- identity : is_enterprise. then ( || identity. clone ( ) ) ,
1066- password,
1067- hw_address : access_point. hw_address ,
1068- secret_tx : self . secret_tx . clone ( ) ,
1069- } ) {
1070- if err. is_disconnected ( ) {
1071- return system_conn ( ) . map ( cosmic:: Action :: App ) ;
1040+ } ) = self . new_connection . take ( ) else {
1041+ return Task :: none ( ) ;
1042+ } ;
1043+
1044+ let Some ( nm) = self . nm . clone ( ) else {
1045+ return cosmic:: task:: message ( Message :: Error (
1046+ "Network manager not initialized" . to_string ( )
1047+ ) ) . map ( cosmic:: Action :: App ) ;
1048+ } ;
1049+
1050+ let ssid = access_point. ssid . to_string ( ) ;
1051+ let password_str = password. unsecure ( ) . to_string ( ) ;
1052+
1053+ self . new_connection = Some ( NewConnectionState :: Waiting ( access_point. clone ( ) ) ) ;
1054+
1055+ return cosmic:: task:: future ( async move {
1056+ let security = match access_point. network_type {
1057+ NetworkType :: Open => WifiSecurity :: Open ,
1058+ NetworkType :: EAP => {
1059+ WifiSecurity :: WpaEap {
1060+ opts : EapOptions {
1061+ identity : identity. clone ( ) ,
1062+ password : password_str,
1063+ anonymous_identity : None ,
1064+ domain_suffix_match : None ,
1065+ ca_cert_path : None ,
1066+ system_ca_certs : true ,
1067+ method : EapMethod :: Peap ,
1068+ phase2 : Phase2 :: Mschapv2 ,
1069+ }
1070+ }
1071+ }
1072+ _ => {
1073+ // All other types (including secured networks) use WPA-PSK
1074+ WifiSecurity :: WpaPsk {
1075+ psk : password_str,
1076+ }
1077+ }
1078+ } ;
1079+
1080+ match nm. connect ( & ssid, security) . await {
1081+ Ok ( ( ) ) => {
1082+ tracing:: info!( "Connected to {}" , ssid) ;
1083+ Message :: Refresh
1084+ }
1085+ Err ( e) => {
1086+ tracing:: error!( "Connection to {} failed: {}" , ssid, e) ;
1087+ let error_msg = match e {
1088+ ConnectionError :: AuthFailed =>
1089+ format ! ( "Wrong password for '{}'" , ssid) ,
1090+ ConnectionError :: NotFound =>
1091+ format ! ( "Network '{}' out of range" , ssid) ,
1092+ ConnectionError :: Timeout =>
1093+ format ! ( "Connection to '{}' timed out" , ssid) ,
1094+ ConnectionError :: DhcpFailed =>
1095+ format ! ( "Connected but failed to get IP address" ) ,
1096+ _ => format ! ( "Failed to connect: {}" , e) ,
1097+ } ;
1098+ Message :: Error ( error_msg)
10721099 }
1073- tracing:: error!( "Failed to authenticate with network manager" ) ;
10741100 }
1075- self . new_connection
1076- . replace ( NewConnectionState :: Waiting ( access_point) ) ;
1077- }
1101+ } ) . map ( cosmic:: Action :: App ) ;
10781102 }
10791103 Message :: ConnectionSettings ( btree_map) => {
10801104 self . nm_state . ssid_to_uuid = btree_map;
@@ -1250,9 +1274,24 @@ impl cosmic::Application for CosmicNetworkApplet {
12501274 } => { }
12511275 } ,
12521276 Message :: NetworkManagerConnect ( connection) => {
1277+ // Initialize nmrs in a separate task
1278+ let init_task = cosmic:: task:: future ( async {
1279+ match nmrs:: NetworkManager :: new ( ) . await {
1280+ Ok ( nm) => {
1281+ tracing:: info!( "nmrs NetworkManager initialized" ) ;
1282+ Message :: NmrsReady ( Some ( nm) )
1283+ }
1284+ Err ( e) => {
1285+ tracing:: warn!( "Failed to initialize nmrs: {}" , e) ;
1286+ Message :: NmrsReady ( None )
1287+ }
1288+ }
1289+ } ) ;
1290+
12531291 return cosmic:: task:: batch ( vec ! [
12541292 self . connect( connection. clone( ) ) ,
12551293 connection_settings( connection) ,
1294+ init_task. map( cosmic:: Action :: App ) ,
12561295 ] ) ;
12571296 }
12581297 Message :: PasswordUpdate ( entered_pw) => {
@@ -1376,6 +1415,12 @@ impl cosmic::Application for CosmicNetworkApplet {
13761415 Message :: CancelVPNConnection => {
13771416 self . nm_state . requested_vpn = None ;
13781417 }
1418+ Message :: NmrsReady ( nm) => {
1419+ self . nm = nm;
1420+ if self . nm . is_some ( ) {
1421+ tracing:: info!( "nmrs ready for WiFi connections" ) ;
1422+ }
1423+ }
13791424 }
13801425 Task :: none ( )
13811426 }
0 commit comments