From 65c641670d81237cb9589ae3ad1d832f9db5253a Mon Sep 17 00:00:00 2001 From: Tochukwu Nkemdilim Date: Thu, 19 Jul 2018 16:14:34 +0100 Subject: [PATCH] Refactor(Project): Change Project Structure --- README.md | 37 ++++++++---- src/connectivity/mod.rs | 19 +++---- src/connectivity/providers/linux.rs | 10 +++- src/connectivity/providers/mod.rs | 2 +- src/connectivity/providers/osx.rs | 81 +++++++-------------------- src/connectivity/providers/windows.rs | 44 +++++++++------ src/hotspot/mod.rs | 13 +++-- src/hotspot/providers/linux.rs | 33 +---------- src/hotspot/providers/mod.rs | 18 ++---- src/hotspot/providers/osx.rs | 17 +----- src/hotspot/providers/windows.rs | 9 +-- src/lib.rs | 34 +++++++---- src/main.rs | 33 +++++------ src/platforms/linux.rs | 44 +++++++-------- src/platforms/mod.rs | 37 +++++++----- src/platforms/osx.rs | 55 +++++++++++++++++- src/platforms/windows.rs | 32 ++++++----- 17 files changed, 268 insertions(+), 250 deletions(-) diff --git a/README.md b/README.md index 93cfa21..10ae954 100644 --- a/README.md +++ b/README.md @@ -50,20 +50,35 @@ fn main() -> Result<(), WifiConnectionError> { } ``` -## To do +## Features -- [x] Bundle windows profile sample as literals -- [x] Add support for Windows -- [x] Add support for linux -- [x] Add disconnect feature +### Windows +- [x] Support for Windows. +- [x] Bundle windows profile sample as literals. +- [x] Add hotspot functionality. - [x] Use `tempfile` crate on windows to generate windows profile temporary file. -- [ ] Add support for OSX +- [ ] Add get network type feature. - [ ] Fix the implementation for `check_if_wifi_is_enabled` for windows. -- [ ] Add get network type feature -- [ ] Add create hotspot functionality -- [ ] Write documentation -- [ ] Write tests -- [ ] Add multi-threaded support +- [ ] Add implementation for WifiInterface trait. + + +### Linux +- [x] Support for linux. +- [x] Add disconnect feature. +- [ ] Add hotspot functionality. +- [ ] Add get network type feature. + +### OsX +- [x] Add support for OSX. +- [ ] Add get network type feature. +- [ ] Add hotspot functionality. + +### General +- [x] Return detailed error messages. +- [ ] Write documentation. **(approx. percentage: 20%)** +- [ ] Update `wifi-CLI` with recent updates. +- [ ] Write tests. +- [ ] Add multi-threaded support. # Contribution diff --git a/src/connectivity/mod.rs b/src/connectivity/mod.rs index fddc39e..00fec30 100644 --- a/src/connectivity/mod.rs +++ b/src/connectivity/mod.rs @@ -1,11 +1,9 @@ #[cfg(target_os = "windows")] mod handlers; - +mod providers; #[cfg(target_os = "windows")] mod stubs; -mod providers; - use platforms::WifiError; use std::{fmt, io}; @@ -17,20 +15,17 @@ pub trait Network: fmt::Debug { fn disconnect(&self) -> Result; } -/// Configuration for a wifi network. -#[derive(Debug, Clone)] -pub struct Config<'a> { - pub interface: Option<&'a str>, -} - #[derive(Debug)] pub enum WifiConnectionError { - SsidNotFound, - IpAssignFailed, + // SsidNotFound, + // IpAssignFailed, + #[cfg(target_os = "windows")] AddNetworkProfileFailed, FailedToConnect(String), FailedToDisconnect(String), - Other { kind: WifiError }, + Other { + kind: WifiError, + }, } impl From for WifiConnectionError { diff --git a/src/connectivity/providers/linux.rs b/src/connectivity/providers/linux.rs index b9d0f05..cf64900 100644 --- a/src/connectivity/providers/linux.rs +++ b/src/connectivity/providers/linux.rs @@ -1,9 +1,15 @@ use connectivity::{Network, WifiConnectionError}; -use platforms::{Connection, Linux}; +use platforms::{Connection, WiFi, WifiError, WifiInterface}; use std::process::Command; -impl Network for Linux { +impl Network for WiFi { fn connect(&mut self, ssid: &str, password: &str) -> Result { + if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? { + return Err(WifiConnectionError::Other { + kind: WifiError::InterfaceDisabled, + }); + } + let output = Command::new("nmcli") .args(&[ "d", diff --git a/src/connectivity/providers/mod.rs b/src/connectivity/providers/mod.rs index 633a7bb..acd57ba 100644 --- a/src/connectivity/providers/mod.rs +++ b/src/connectivity/providers/mod.rs @@ -4,5 +4,5 @@ mod linux; #[cfg(target_os = "osx")] mod osx; -// #[cfg(target_os = "windows")] +#[cfg(target_os = "windows")] mod windows; diff --git a/src/connectivity/providers/osx.rs b/src/connectivity/providers/osx.rs index 6321a96..5a40310 100644 --- a/src/connectivity/providers/osx.rs +++ b/src/connectivity/providers/osx.rs @@ -1,40 +1,40 @@ use connectivity::{Network, WifiConnectionError}; +use platforms::{Connection, WiFi, WifiError, WifiInterface}; use std::process::Command; -#[derive(Debug)] -pub struct OSX { - pub name: String, - interface: String, -} - -impl OSX { - #[cfg(target_os = "windows")] - pub fn new(name: &str, interface: Option<&str>) -> Self { - OSX { - name: name.into(), - interface: interface.unwrap_or("en0").into(), +impl Network for WiFi { + fn connect(&mut self, ssid: &str, password: &str) -> Result { + if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? { + return Err(WifiConnectionError::Other { + kind: WifiError::InterfaceDisabled, + }); } - } -} -impl Network for OSX { - fn connect(&self, password: &str) -> Result { let output = Command::new("networksetup") - .args(&["-setairportnetwork", &self.interface, &self.name, &password]) + .args(&["-setairportnetwork", &self.interface, &ssid, &password]) .output() .map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?; - Ok(String::from_utf8_lossy(&output.stdout) + if !String::from_utf8_lossy(&output.stdout) .as_ref() - .contains("successfully activated")) + .contains("successfully activated") + { + return Ok(false); + } + + self.connection = Some(Connection { + ssid: String::from(ssid), + }); + + Ok(true) } fn disconnect(&self) -> Result { let output = Command::new("networksetup") .args(&[ "-removepreferredwirelessnetwork", - &self.interface, - &self.name, + &*self.interface, + &*self.connection.as_ref().unwrap().ssid, ]) .output() .map_err(|err| WifiConnectionError::FailedToDisconnect(format!("{}", err)))?; @@ -43,43 +43,4 @@ impl Network for OSX { .as_ref() .contains("disconnect")) } - - fn is_wifi_enabled(&self) -> bool { - let output = Command::new("networksetup") - .args(&["radio", "wifi"]) - .output(); - - if let Err(_) = output { - return false; - } - - String::from_utf8_lossy(&output.unwrap().stdout) - .replace(" ", "") - .replace("\n", "") - .contains("enabled") - } - - fn connnection_up(&self) -> bool { - let output = Command::new("networksetup") - .args(&["-setairportpower", &self.interface, "on"]) - .output(); - - if let Err(_) = output { - return false; - } - - false - } - - fn connnection_down(&self) -> bool { - let output = Command::new("networksetup") - .args(&["-setairportpower", &self.interface, "off"]) - .output(); - - if let Err(_) = output { - return false; - } - - false - } } diff --git a/src/connectivity/providers/windows.rs b/src/connectivity/providers/windows.rs index 6135c57..ffce2b2 100644 --- a/src/connectivity/providers/windows.rs +++ b/src/connectivity/providers/windows.rs @@ -1,21 +1,14 @@ use connectivity::handlers::NetworkXmlProfileHandler; use connectivity::{Network, WifiConnectionError}; -use platforms::Windows; +use platforms::{Connection, WiFi, WifiError, WifiInterface}; use std::process::Command; -impl Windows { - #[cfg(target_os = "windows")] - pub fn new(name: &str, _interface: Option<&str>) -> Self { - Windows { - name: String::from(name), - } - } - - pub(crate) fn add_profile(&self, password: &str) -> Result<(), WifiConnectionError> { +impl WiFi { + fn add_profile(ssid: &str, password: &str) -> Result<(), WifiConnectionError> { let mut handler = NetworkXmlProfileHandler::new(); handler.content = handler .content - .replace("{SSID}", &self.name) + .replace("{SSID}", ssid) .replace("{password}", password); let temp_file = handler.write_to_temp_file()?; @@ -36,17 +29,36 @@ impl Windows { } impl Network for Windows { - fn connect(&self, password: &str) -> Result { - self.add_profile(password)?; + fn connect(&mut self, ssid: &str, password: &str) -> Result { + if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? { + return Err(WifiConnectionError::Other { + kind: WifiError::InterfaceDisabled, + }); + } + + Self::add_profile(ssid, password)?; let output = Command::new("netsh") - .args(&["wlan", "connect", &format!("name={}", self.name)]) + .args(&[ + "wlan", + "connect", + &format!("name={}", *self.connection.unwrap().ssid), + ]) .output() .map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?; - Ok(String::from_utf8_lossy(&output.stdout) + if !String::from_utf8_lossy(&output.stdout) .as_ref() - .contains("successfully activated")) + .contains("successfully activated") + { + return Ok(false); + } + + self.connection = Some(Connection { + ssid: String::from(ssid), + }); + + Ok(true) } fn disconnect(&self) -> Result { diff --git a/src/hotspot/mod.rs b/src/hotspot/mod.rs index 3b3747c..f645fcb 100644 --- a/src/hotspot/mod.rs +++ b/src/hotspot/mod.rs @@ -1,6 +1,5 @@ mod providers; -// pub use self::providers::Machine; use platforms::{WifiError, WifiInterface}; use std::fmt; @@ -15,13 +14,19 @@ pub trait WifiHotspot: fmt::Debug + WifiInterface { /// Creates wifi hotspot service for host machine. This only creats the wifi network, /// and isn't responsible for initiating the serving of the wifi network process. /// To begin serving the hotspot, use ```start_hotspot()```. - fn create_hotspot(ssid: &str, password: &str) -> Result; + fn create_hotspot(ssid: &str, password: &str) -> Result { + unimplemented!(); + } /// Start serving publicly an already created wifi hotspot. - fn start_hotspot() -> Result; + fn start_hotspot() -> Result { + unimplemented!(); + } /// Stop serving a wifi network. /// /// > All users connected will automatically be disconnected. - fn stop_hotspot() -> Result; + fn stop_hotspot() -> Result { + unimplemented!(); + } } diff --git a/src/hotspot/providers/linux.rs b/src/hotspot/providers/linux.rs index 8101d69..263d102 100644 --- a/src/hotspot/providers/linux.rs +++ b/src/hotspot/providers/linux.rs @@ -1,31 +1,4 @@ -use hotspot::{WifiHotspot, WifiHotspotError}; -use platforms::Linux; +use hotspot::WifiHotspot; +use platforms::WiFi; -// #[derive(Debug)] -// pub struct Linux { -// pub name: String, -// interface: String, -// } - -// impl Linux { -// pub fn new(name: &str, interface: Option<&str>) -> Self { -// Linux { -// name: name.into(), -// interface: interface.unwrap_or("wlan0").into(), -// } -// } -// } - -impl WifiHotspot for Linux { - fn create_hotspot(ssid: &str, password: &str) -> Result { - unimplemented!(); - } - - fn start_hotspot() -> Result { - unimplemented!(); - } - - fn stop_hotspot() -> Result { - unimplemented!(); - } -} +impl WifiHotspot for WiFi {} diff --git a/src/hotspot/providers/mod.rs b/src/hotspot/providers/mod.rs index 267043e..1b93b48 100644 --- a/src/hotspot/providers/mod.rs +++ b/src/hotspot/providers/mod.rs @@ -1,14 +1,8 @@ -// #[cfg(target_os = "windows")] -// mod windows; -// #[cfg(target_os = "windows")] -// pub use self::windows::Windows as Machine; +#[cfg(target_os = "windows")] +mod windows; -// #[cfg(target_os = "linux")] -// mod linux; -// #[cfg(target_os = "linux")] -// pub use self::linux as Machine; +#[cfg(target_os = "linux")] +mod linux; -// #[cfg(target_os = "osx")] -// mod osx; -// #[cfg(target_os = "osx")] -// pub use self::osx::OSX as Machine; +#[cfg(target_os = "osx")] +mod osx; diff --git a/src/hotspot/providers/osx.rs b/src/hotspot/providers/osx.rs index 98174c2..0a6300d 100644 --- a/src/hotspot/providers/osx.rs +++ b/src/hotspot/providers/osx.rs @@ -1,18 +1,5 @@ use connectivity::{Network, WifiConnectionError}; +use platforms::WiFi; use std::process::Command; -#[derive(Debug)] -pub struct OSX { - pub name: String, - interface: String, -} - -impl OSX { - #[cfg(target_os = "windows")] - pub fn new(name: &str, interface: Option<&str>) -> Self { - OSX { - name: name.into(), - interface: interface.unwrap_or("en0").into(), - } - } -} +impl WifiHotspot for WiFi {} diff --git a/src/hotspot/providers/windows.rs b/src/hotspot/providers/windows.rs index 3e9ea86..a592c60 100644 --- a/src/hotspot/providers/windows.rs +++ b/src/hotspot/providers/windows.rs @@ -2,15 +2,10 @@ use connectivity::handlers::NetworkXmlProfileHandler; use connectivity::{ Network, WifiConnectionError, WifiError, WifiHotspot, WifiHotspotError, WifiInterface, }; -use platforms::Windows; +use platforms::WiFi; use std::process::Command; -// #[derive(Debug)] -// pub(crate) struct Windows { -// name: String, -// } - -impl WifiHotspot for Windows { +impl WifiHotspot for WiFi { fn create_hotspot(ssid: &str, password: &str) -> Result { let output = Command::new("netsh") .args(&[ diff --git a/src/lib.rs b/src/lib.rs index a0abfba..320711a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,21 +4,33 @@ mod connectivity; mod hotspot; mod platforms; -pub use connectivity::{Config, *}; -pub use hotspot::*; +pub mod prelude { + pub use connectivity::*; + pub use hotspot::*; +} + +pub use platforms::*; #[cfg(test)] mod tests { - use super::*; + use super::prelude::*; + use super::*; - #[test] - fn connect_to_wifi_failed() { - // let config = Some(Config { - // interface: Some("wlo1"), - // }); + #[test] + fn connect_to_wifi_failed() { + let config = Some(Config { + interface: Some("wlo1"), + }); - // let wifi = WiFi::new("hello", config).unwrap(); + let mut wifi = WiFi::new(config); - // assert_eq!(wifi.connect("password").unwrap(), false); - } + assert_eq!(wifi.connect("ssid", "password").unwrap(), false); + } + + #[test] + fn create_hotspot() { + let hotspot_created = WiFi::create_hotspot("hello", "hi").unwrap(); + + assert_eq!(hotspot_created, true); + } } diff --git a/src/main.rs b/src/main.rs index 93a9de8..6d14bd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,26 +2,27 @@ mod connectivity; mod hotspot; mod platforms; -pub use connectivity::{Config, WifiConnectionError}; +use connectivity::{Network, WifiConnectionError}; +use platforms::{Config, WiFi}; fn main() -> Result<(), WifiConnectionError> { - // let config = Some(Config { - // interface: Some("wlo1"), - // }); + let config = Some(Config { + interface: Some("wlo1"), + }); - // let wifi = WiFi::new("AndroidAPSD22", config); + let mut wifi = WiFi::new(config); - // match wifi.connect("belm4235") { - // Ok(result) => println!( - // "{}", - // if result == true { - // "Connection Successfull." - // } else { - // "Invalid password." - // } - // ), - // Err(err) => println!("The following error occurred: {:?}", err), - // } + match wifi.connect("AndroidAPSD22", "belm4235") { + Ok(result) => println!( + "{}", + if result == true { + "Connection Successfull." + } else { + "Invalid password." + } + ), + Err(err) => println!("The following error occurred: {:?}", err), + } Ok(()) } diff --git a/src/platforms/linux.rs b/src/platforms/linux.rs index 1983584..387a09d 100644 --- a/src/platforms/linux.rs +++ b/src/platforms/linux.rs @@ -1,58 +1,56 @@ -use platforms::{WifiError, WifiInterface}; -use std::collections::HashMap; +use platforms::{Config, WifiError, WifiInterface}; use std::process::Command; #[derive(Debug)] -pub(crate) struct Connection { - pub ssid: String, +pub struct Connection { + pub(crate) ssid: String, } #[derive(Debug)] -pub(crate) struct Linux { - hotspot: Option>, - pub connection: Option, - pub interface: String, +pub struct Linux { + pub(crate) connection: Option, + pub(crate) interface: String, } impl Linux { - pub fn new(name: &str, interface: Option<&str>) -> Self { + pub fn new(config: Option) -> Self { Linux { - hotspot: None, connection: None, - interface: String::from("wlan0"), + interface: config.map_or("wlan0".to_string(), |cfg| { + cfg.interface.unwrap_or("wlan0").to_string() + }), } } } impl WifiInterface for Linux { - fn is_wifi_enabled() -> bool { - let output = Command::new("nmcli").args(&["radio", "wifi"]).output(); + fn is_wifi_enabled() -> Result { + let output = Command::new("nmcli") + .args(&["radio", "wifi"]) + .output() + .map_err(|err| WifiError::IoError(err))?; - if let Err(_) = output { - return false; - } - - String::from_utf8_lossy(&output.unwrap().stdout) + Ok(String::from_utf8_lossy(&output.stdout) .replace(" ", "") .replace("\n", "") - .contains("enabled") + .contains("enabled")) } - fn turn_on() -> Result { + fn turn_on() -> Result<(), WifiError> { let _output = Command::new("nmcli") .args(&["radio", "wifi", "on"]) .output() .map_err(|err| WifiError::IoError(err))?; - Ok(true) + Ok(()) } - fn turn_off() -> Result { + fn turn_off() -> Result<(), WifiError> { let _output = Command::new("nmcli") .args(&["radio", "wifi", "off"]) .output() .map_err(|err| WifiError::IoError(err))?; - Ok(true) + Ok(()) } } diff --git a/src/platforms/mod.rs b/src/platforms/mod.rs index 9266b27..f95a5cd 100644 --- a/src/platforms/mod.rs +++ b/src/platforms/mod.rs @@ -1,38 +1,49 @@ #[cfg(target_os = "linux")] mod linux; - #[cfg(target_os = "osx")] mod osx; - -// #[cfg(target_os = "windows")] +#[cfg(target_os = "windows")] mod windows; #[cfg(target_os = "linux")] -pub(crate) use self::linux::{Connection, Linux}; - +pub use self::linux::{Connection, Linux as WiFi}; #[cfg(target_os = "osx")] -pub use self::osx::OSX; - -// #[cfg(target_os = "windows")] -pub use self::windows::Windows; +pub use self::osx::{Connection, Osx as WiFi}; +#[cfg(target_os = "windows")] +pub use self::windows::{Connection, Windows as WiFi}; use std::{fmt, io}; +/// Configuration for a wifi network. +#[derive(Debug, Clone)] +pub struct Config<'a> { + pub interface: Option<&'a str>, +} + #[derive(Debug)] pub enum WifiError { - OsNotSupported, + // OsNotSupported, InterfaceDisabled, + + #[cfg(target_os = "windows")] InterfaceFailedToOn, + IoError(io::Error), } pub trait WifiInterface: fmt::Debug { /// Checks if the wifi interface on host machine is enables. - fn is_wifi_enabled() -> bool; + fn is_wifi_enabled() -> Result { + unimplemented!(); + } /// Turns on the wifi interface of host machine. - fn turn_on() -> Result; + fn turn_on() -> Result<(), WifiError> { + unimplemented!(); + } // Turns off the wifi interface of host machine. - fn turn_off() -> Result; + fn turn_off() -> Result<(), WifiError> { + unimplemented!(); + } } diff --git a/src/platforms/osx.rs b/src/platforms/osx.rs index 9eef58b..7c22a93 100644 --- a/src/platforms/osx.rs +++ b/src/platforms/osx.rs @@ -1,7 +1,56 @@ -use std::collections::HashMap; +use platforms::{WifiError, WifiInterface}; +use std::process::Command; + +#[derive(Debug)] +pub struct Connection { + pub(crate) ssid: String, +} #[derive(Debug)] pub struct Osx { - hotspot: Option, - connectivity: Option, + pub(crate) connection: Option, + pub(crate) interface: String, +} + +impl Osx { + pub fn new(name: &str, config: Option) -> Self { + Osx { + connection: None, + interface: config.map_or("en0".to_string(), |cfg| { + cfg.interface.unwrap_or("en0").to_string() + }), + } + } +} + +impl WifiInterface for Osx { + fn is_wifi_enabled() -> Result { + let output = Command::new("networksetup") + .args(&["radio", "wifi"]) + .output() + .map_err(|err| WifiError::IoError(err))?; + + Ok(String::from_utf8_lossy(&output.stdout) + .replace(" ", "") + .replace("\n", "") + .contains("enabled")) + } + + fn turn_on() -> Result<(), WifiError> { + let output = Command::new("networksetup") + .args(&["-setairportpower", "en0", "on"]) + .output() + .map_err(|err| WifiError::IoError(err))?; + + Ok(()) + } + + fn turn_off() -> Result<(), WifiError> { + let output = Command::new("networksetup") + .args(&["-setairportpower", "en0", "off"]) + .output() + .map_err(|err| WifiError::IoError(err))?; + + Ok(()) + } } diff --git a/src/platforms/windows.rs b/src/platforms/windows.rs index 25c5e8d..b0937a1 100644 --- a/src/platforms/windows.rs +++ b/src/platforms/windows.rs @@ -1,22 +1,26 @@ +use platforms::{WifiError, WifiInterface}; use std::collections::HashMap; -use + +#[derive(Debug)] +pub struct Connection { + pub(crate) ssid: String, +} #[derive(Debug)] pub struct Windows { - hotspot: Option, - connectivity: Option, + pub(crate) connection: Option, + pub(crate) interface: String, } -impl WifiInterface for Windows { - fn is_wifi_enabled() -> bool { - unimplemented!() - } - - fn turn_on() -> Result { - unimplemented!() - } - - fn turn_off() -> Result { - unimplemented!() +impl Windows { + pub fn new(name: &str, config: Option) -> Self { + Windows { + connection: None, + interface: config.map_or("wlan0".to_string(), |cfg| { + cfg.interface.unwrap_or("wlan0").to_string() + }), + } } } + +impl WifiInterface for Windows {}