peach-workspace/peach-web/src/routes/settings/network.rs

1063 lines
38 KiB
Rust

use log::{debug, warn};
use rocket::{
get,
post,
request::FlashMessage,
form::{Form, FromForm},
response::{Flash, Redirect},
uri, UriDisplayQuery,
};
use rocket::serde::json::{json, Json};
use rocket_dyn_templates::Template;
use rocket::serde::{Deserialize, Serialize};
use std::collections::HashMap;
use peach_lib::network_client;
use peach_lib::network_client::{AccessPoint, Networks, Scan};
use peach_lib::stats_client::Traffic;
use crate::utils::monitor;
use crate::utils::monitor::{Alert, Data, Threshold};
use crate::utils::build_json_response;
use crate::routes::authentication::Authenticated;
use rocket::serde::json::Value;
// STRUCTS USED BY NETWORK ROUTES
#[derive(Debug, Deserialize, FromForm, UriDisplayQuery)]
pub struct Ssid {
pub ssid: String,
}
#[derive(Debug, Deserialize, FromForm)]
pub struct WiFi {
pub ssid: String,
pub pass: String,
}
// HELPERS AND ROUTES FOR /network/wifi/usage/reset
#[get("/network/wifi/usage/reset")]
pub fn wifi_usage_reset(auth: Authenticated) -> Flash<Redirect> {
let url = uri!(wifi_usage);
match monitor::reset_data() {
Ok(_) => Flash::success(Redirect::to(url), "Reset stored network traffic total"),
Err(_) => Flash::error(
Redirect::to(url),
"Failed to reset stored network traffic total",
),
}
}
#[post("/network/wifi/connect", data = "<network>")]
pub fn connect_wifi(network: Form<Ssid>, auth: Authenticated) -> Flash<Redirect> {
let ssid = &network.ssid;
let url = uri!(network_detail(ssid = ssid));
match network_client::id("wlan0", ssid) {
Ok(id) => match network_client::connect(&id, "wlan0") {
Ok(_) => Flash::success(Redirect::to(url), "Connected to chosen network"),
Err(_) => Flash::error(Redirect::to(url), "Failed to connect to chosen network"),
},
Err(_) => Flash::error(Redirect::to(url), "Failed to retrieve the network ID"),
}
}
#[post("/network/wifi/disconnect", data = "<network>")]
pub fn disconnect_wifi(network: Form<Ssid>, auth: Authenticated) -> Flash<Redirect> {
let ssid = &network.ssid;
let url = uri!(network_home);
match network_client::disable("wlan0", ssid) {
Ok(_) => Flash::success(Redirect::to(url), "Disconnected from WiFi network"),
Err(_) => Flash::error(Redirect::to(url), "Failed to disconnect from WiFi network"),
}
}
#[post("/network/wifi/forget", data = "<network>")]
pub fn forget_wifi(network: Form<Ssid>, auth: Authenticated) -> Flash<Redirect> {
let ssid = &network.ssid;
let url = uri!(network_home);
match network_client::forget("wlan0", ssid) {
Ok(_) => Flash::success(Redirect::to(url), "WiFi credentials removed"),
Err(_) => Flash::error(
Redirect::to(url),
"Failed to remove WiFi credentials".to_string(),
),
}
}
#[get("/network/wifi/modify?<ssid>")]
pub fn wifi_password(ssid: &str, flash: Option<FlashMessage>, auth: Authenticated) -> Template {
let mut context = NetworkAddContext {
back: Some("/network/wifi".to_string()),
flash_name: None,
flash_msg: None,
selected: Some(ssid.to_string()),
title: Some("Update WiFi Password".to_string()),
};
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_modify", &context)
}
#[post("/network/wifi/modify", data = "<wifi>")]
pub fn wifi_set_password(wifi: Form<WiFi>, auth: Authenticated) -> Flash<Redirect> {
let ssid = &wifi.ssid;
let pass = &wifi.pass;
let url = uri!(network_detail(ssid = ssid));
match network_client::update("wlan0", ssid, pass) {
Ok(_) => Flash::success(Redirect::to(url), "WiFi password updated".to_string()),
Err(_) => Flash::error(
Redirect::to(url),
"Failed to update WiFi password".to_string(),
),
}
}
// HELPERS AND ROUTES FOR /network
#[derive(Debug, Serialize)]
pub struct NetworkContext {
pub ap_ip: String,
pub ap_ssid: String,
pub ap_state: String,
pub ap_traffic: Option<Traffic>,
pub wlan_ip: String,
pub wlan_rssi: Option<String>,
pub wlan_scan: Option<Vec<Scan>>,
pub wlan_ssid: String,
pub wlan_state: String,
pub wlan_status: String,
pub wlan_traffic: Option<Traffic>,
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
// allows for passing in the ssid of a chosen access point
// this is used in the network_detail template
pub selected: Option<String>,
// page title for header in navbar
pub title: Option<String>,
// url for back-arrow link
pub back: Option<String>,
}
impl NetworkContext {
pub fn build() -> NetworkContext {
let ap_ip = match network_client::ip("ap0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
let ap_ssid = match network_client::ssid("ap0") {
Ok(ssid) => ssid,
Err(_) => "Not currently activated".to_string(),
};
let ap_state = match network_client::state("ap0") {
Ok(state) => state,
Err(_) => "Interface unavailable".to_string(),
};
let ap_traffic = match network_client::traffic("ap0") {
Ok(traffic) => {
let mut t = traffic;
// modify traffic values & assign measurement unit
// based on received and transmitted values
// if received > 999 MB, convert it to GB
if t.received > 1_047_527_424 {
t.received /= 1_073_741_824;
t.rx_unit = Some("GB".to_string());
} else if t.received > 0 {
// otherwise, convert it to MB
t.received = (t.received / 1024) / 1024;
t.rx_unit = Some("MB".to_string());
} else {
t.received = 0;
t.rx_unit = Some("MB".to_string());
}
if t.transmitted > 1_047_527_424 {
t.transmitted /= 1_073_741_824;
t.tx_unit = Some("GB".to_string());
} else if t.transmitted > 0 {
t.transmitted = (t.transmitted / 1024) / 1024;
t.tx_unit = Some("MB".to_string());
} else {
t.transmitted = 0;
t.tx_unit = Some("MB".to_string());
}
Some(t)
}
Err(_) => None,
};
let wlan_ip = match network_client::ip("wlan0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
let wlan_rssi = match network_client::rssi_percent("wlan0") {
Ok(rssi) => Some(rssi),
Err(_) => None,
};
let wlan_scan = match network_client::available_networks("wlan0") {
Ok(networks) => {
let scan: Vec<Scan> = serde_json::from_str(networks.as_str())
.expect("Failed to deserialize scan_networks response");
Some(scan)
}
Err(_) => None,
};
let wlan_ssid = match network_client::ssid("wlan0") {
Ok(ssid) => ssid,
Err(_) => "Not connected".to_string(),
};
let wlan_state = match network_client::state("wlan0") {
Ok(state) => state,
Err(_) => "Interface unavailable".to_string(),
};
let wlan_status = match network_client::status("wlan0") {
Ok(status) => status,
Err(_) => "Interface unavailable".to_string(),
};
let wlan_traffic = match network_client::traffic("wlan0") {
Ok(traffic) => {
let mut t = traffic;
// modify traffic values & assign measurement unit
// based on received and transmitted values
// if received > 999 MB, convert it to GB
if t.received > 1_047_527_424 {
t.received /= 1_073_741_824;
t.rx_unit = Some("GB".to_string());
} else if t.received > 0 {
// otherwise, convert it to MB
t.received = (t.received / 1024) / 1024;
t.rx_unit = Some("MB".to_string());
} else {
t.received = 0;
t.rx_unit = Some("MB".to_string());
}
if t.transmitted > 1_047_527_424 {
t.transmitted /= 1_073_741_824;
t.tx_unit = Some("GB".to_string());
} else if t.transmitted > 0 {
t.transmitted = (t.transmitted / 1024) / 1024;
t.tx_unit = Some("MB".to_string());
} else {
t.transmitted = 0;
t.tx_unit = Some("MB".to_string());
}
Some(t)
}
Err(_) => None,
};
NetworkContext {
ap_ip,
ap_ssid,
ap_state,
ap_traffic,
wlan_ip,
wlan_rssi,
wlan_scan,
wlan_ssid,
wlan_state,
wlan_status,
wlan_traffic,
flash_name: None,
flash_msg: None,
selected: None,
title: None,
back: None,
}
}
}
#[get("/network")]
pub fn network_home(flash: Option<FlashMessage>, auth: Authenticated) -> Template {
// assign context through context_builder call
let mut context = NetworkContext::build();
// set back button (nav) url
context.back = Some("/".to_string());
// set page title
context.title = Some("Network Configuration".to_string());
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_card", &context)
}
// HELPERS AND ROUTES FOR /network/ap/activate
#[get("/network/ap/activate")]
pub fn deploy_ap(auth: Authenticated) -> Flash<Redirect> {
// activate the wireless access point
debug!("Activating WiFi access point.");
match network_client::activate_ap() {
Ok(_) => Flash::success(Redirect::to("/network"), "Activated WiFi access point"),
Err(_) => Flash::error(
Redirect::to("/network"),
"Failed to activate WiFi access point",
),
}
}
// HELPERS AND ROUTES FOR /network/wifi
#[derive(Debug, Serialize)]
pub struct NetworkListContext {
pub ap_state: String,
pub back: Option<String>,
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub title: Option<String>,
pub wlan_networks: HashMap<String, String>,
pub wlan_ssid: String,
}
impl NetworkListContext {
pub fn build() -> NetworkListContext {
// list of networks saved in the wpa_supplicant.conf
let wlan_list = match network_client::saved_networks() {
Ok(ssids) => {
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())
.expect("Failed to deserialize scan_list response");
networks
}
Err(_) => Vec::new(),
};
// list of networks currently in range (online & accessible)
let wlan_scan = match network_client::available_networks("wlan0") {
Ok(networks) => {
let scan: Vec<Networks> = serde_json::from_str(networks.as_str())
.expect("Failed to deserialize scan_networks response");
scan
}
Err(_) => Vec::new(),
};
let wlan_ssid = match network_client::ssid("wlan0") {
Ok(ssid) => ssid,
Err(_) => "Not connected".to_string(),
};
// create a hashmap to combine wlan_list & wlan_scan without repetition
let mut wlan_networks = HashMap::new();
for ap in wlan_scan {
wlan_networks.insert(ap.ssid, "Available".to_string());
}
for network in wlan_list {
// insert ssid (with state) only if it doesn't already exist
wlan_networks
.entry(network.ssid)
.or_insert_with(|| "Not in range".to_string());
}
let ap_state = match network_client::state("ap0") {
Ok(state) => state,
Err(_) => "Interface unavailable".to_string(),
};
NetworkListContext {
ap_state,
back: None,
flash_msg: None,
flash_name: None,
title: None,
wlan_networks,
wlan_ssid,
}
}
}
#[get("/network/wifi")]
pub fn wifi_list(flash: Option<FlashMessage>, auth: Authenticated) -> Template {
// assign context through context_builder call
let mut context = NetworkListContext::build();
context.back = Some("/network".to_string());
context.title = Some("WiFi Networks".to_string());
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_list", &context)
}
// HELPERS AND ROUTES FOR /network/wifi<ssid>
#[derive(Debug, Serialize)]
pub struct NetworkDetailContext {
pub back: Option<String>,
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub saved_aps: Vec<Networks>,
pub selected: Option<String>,
pub title: Option<String>,
pub wlan_ip: String,
pub wlan_networks: HashMap<String, AccessPoint>,
pub wlan_rssi: Option<String>,
pub wlan_ssid: String,
pub wlan_state: String,
pub wlan_status: String,
pub wlan_traffic: Option<Traffic>,
}
impl NetworkDetailContext {
pub fn build() -> NetworkDetailContext {
let wlan_ip = match network_client::ip("wlan0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
// list of networks saved in wpa_supplicant.conf
let wlan_list = match network_client::saved_networks() {
Ok(ssids) => {
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())
.expect("Failed to deserialize scan_list response");
networks
}
Err(_) => Vec::new(),
};
// list of networks saved in wpa_supplicant.conf
// HACK: we're running the same function twice (wlan_list)
// see if we can implement clone for Vec<Networks> instead
let saved_aps = match network_client::saved_networks() {
Ok(ssids) => {
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())
.expect("Failed to deserialize scan_list response");
networks
}
Err(_) => Vec::new(),
};
let wlan_rssi = match network_client::rssi_percent("wlan0") {
Ok(rssi) => Some(rssi),
Err(_) => None,
};
// list of networks currently in range (online & accessible)
let wlan_scan = match network_client::available_networks("wlan0") {
Ok(networks) => {
let scan: Vec<Scan> = serde_json::from_str(networks.as_str())
.expect("Failed to deserialize scan_networks response");
scan
}
Err(_) => Vec::new(),
};
let wlan_ssid = match network_client::ssid("wlan0") {
Ok(ssid) => ssid,
Err(_) => "Not connected".to_string(),
};
let wlan_state = match network_client::state("wlan0") {
Ok(state) => state,
Err(_) => "Interface unavailable".to_string(),
};
let wlan_status = match network_client::status("wlan0") {
Ok(status) => status,
Err(_) => "Interface unavailable".to_string(),
};
let wlan_traffic = match network_client::traffic("wlan0") {
Ok(traffic) => {
let mut t = traffic;
// modify traffic values & assign measurement unit
// based on received and transmitted values
// if received > 999 MB, convert it to GB
if t.received > 1_047_527_424 {
t.received /= 1_073_741_824;
t.rx_unit = Some("GB".to_string());
} else if t.received > 0 {
// otherwise, convert it to MB
t.received = (t.received / 1024) / 1024;
t.rx_unit = Some("MB".to_string());
} else {
t.received = 0;
t.rx_unit = Some("MB".to_string());
}
if t.transmitted > 1_047_527_424 {
t.transmitted /= 1_073_741_824;
t.tx_unit = Some("GB".to_string());
} else if t.transmitted > 0 {
t.transmitted = (t.transmitted / 1024) / 1024;
t.tx_unit = Some("MB".to_string());
} else {
t.transmitted = 0;
t.tx_unit = Some("MB".to_string());
}
Some(t)
}
Err(_) => None,
};
// create a hashmap to combine wlan_list & wlan_scan without repetition
let mut wlan_networks = HashMap::new();
for ap in wlan_scan {
let ssid = ap.ssid.clone();
let rssi = ap.signal_level.clone();
// parse the string to a signed integer (for math)
let rssi_parsed = rssi.parse::<i32>().unwrap();
// perform rssi (dBm) to quality (%) conversion
let quality_percent = 2 * (rssi_parsed + 100);
let ap_detail = AccessPoint {
detail: Some(ap),
state: "Available".to_string(),
signal: Some(quality_percent),
};
wlan_networks.insert(ssid, ap_detail);
}
for network in wlan_list {
// avoid repetition by checking that ssid is not already in list
if !wlan_networks.contains_key(&network.ssid) {
let ssid = network.ssid.clone();
let net_detail = AccessPoint {
detail: None,
state: "Not in range".to_string(),
signal: None,
};
wlan_networks.insert(ssid, net_detail);
}
}
NetworkDetailContext {
back: None,
flash_name: None,
flash_msg: None,
saved_aps,
selected: None,
title: None,
wlan_ip,
wlan_networks,
wlan_rssi,
wlan_ssid,
wlan_state,
wlan_status,
wlan_traffic,
}
}
}
#[get("/network/wifi?<ssid>")]
pub fn network_detail(ssid: &str, flash: Option<FlashMessage>, auth: Authenticated) -> Template {
// assign context through context_builder call
let mut context = NetworkDetailContext::build();
context.back = Some("/network/wifi".to_string());
context.title = Some("WiFi Network".to_string());
context.selected = Some(ssid.to_string());
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_detail", &context)
}
// HELPERS AND ROUTES FOR /network/wifi/activate
#[get("/network/wifi/activate")]
pub fn deploy_client(auth: Authenticated) -> Flash<Redirect> {
// activate the wireless client
debug!("Activating WiFi client mode.");
match network_client::activate_client() {
Ok(_) => Flash::success(Redirect::to("/network"), "Activated WiFi client"),
Err(_) => Flash::error(Redirect::to("/network"), "Failed to activate WiFi client"),
}
}
// HELPERS AND ROUTES FOR /network/wifi/add
#[get("/network/wifi/add")]
pub fn network_add_wifi(flash: Option<FlashMessage>, auth: Authenticated) -> Template {
let mut context = NetworkContext::build();
// set back icon link to network route
context.back = Some("/network".to_string());
context.title = Some("Add WiFi Network".to_string());
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_add", &context)
}
// used in /network/wifi/add?<ssid>
#[derive(Debug, Serialize)]
pub struct NetworkAddContext {
pub back: Option<String>,
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub selected: Option<String>,
pub title: Option<String>,
}
impl NetworkAddContext {
pub fn build() -> NetworkAddContext {
NetworkAddContext {
back: None,
flash_name: None,
flash_msg: None,
selected: None,
title: None,
}
}
}
#[get("/network/wifi/add?<ssid>")]
pub fn network_add_ssid(ssid: &str, flash: Option<FlashMessage>, auth: Authenticated) -> Template {
let mut context = NetworkAddContext::build();
context.back = Some("/network/wifi".to_string());
context.selected = Some(ssid.to_string());
context.title = Some("Add WiFi Network".to_string());
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_add", &context)
}
#[post("/network/wifi/add", data = "<wifi>")]
pub fn add_credentials(wifi: Form<WiFi>, auth: Authenticated) -> Template {
// check if the credentials already exist for this access point
// note: this is nicer but it's an unstable feature:
// if check_saved_aps(&wifi.ssid).contains(true)
// use unwrap_or instead, set value to false if err is returned
let creds_exist = network_client::saved_ap(&wifi.ssid).unwrap_or(false);
if creds_exist {
let mut context = NetworkAddContext::build();
context.back = Some("/network".to_string());
context.flash_name = Some("error".to_string());
context.flash_msg =
Some("Network credentials already exist for this access point".to_string());
context.title = Some("Add WiFi Network".to_string());
// return early from handler with "creds already exist" message
return Template::render("network_add", &context);
};
// if credentials not found, generate and write wifi config to wpa_supplicant
match network_client::add(&wifi.ssid, &wifi.pass) {
Ok(_) => {
debug!("Added WiFi credentials.");
// force reread of wpa_supplicant.conf file with new credentials
match network_client::reconfigure() {
Ok(_) => debug!("Successfully reconfigured wpa_supplicant"),
Err(_) => warn!("Failed to reconfigure wpa_supplicant"),
}
let mut context = NetworkAddContext::build();
context.back = Some("/network".to_string());
context.flash_name = Some("success".to_string());
context.flash_msg = Some("Added WiFi credentials".to_string());
context.title = Some("Add WiFi Network".to_string());
Template::render("network_add", &context)
}
Err(_) => {
debug!("Failed to add WiFi credentials.");
let mut context = NetworkAddContext::build();
context.back = Some("/network".to_string());
context.flash_name = Some("error".to_string());
context.flash_msg = Some("Failed to add WiFi credentials".to_string());
context.title = Some("Add WiFi Network".to_string());
Template::render("network_add", &context)
}
}
}
// HELPERS AND ROUTES FOR WIFI USAGE
#[derive(Debug, Serialize)]
pub struct NetworkAlertContext {
pub alert: Alert,
pub back: Option<String>,
pub data_total: Data, // combined stored and current wifi traffic in bytes
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub threshold: Threshold,
pub title: Option<String>,
pub traffic: Traffic, // current wifi traffic in bytes (since boot)
}
impl NetworkAlertContext {
pub fn build() -> NetworkAlertContext {
let alert = monitor::get_alerts().unwrap();
// stored wifi data values as bytes
let stored_traffic = monitor::get_data().unwrap();
let threshold = monitor::get_thresholds().unwrap();
// current wifi traffic values as bytes
let traffic = match network_client::traffic("wlan0") {
Ok(t) => t,
Err(_) => Traffic {
received: 0,
transmitted: 0,
rx_unit: None,
tx_unit: None,
},
};
let current_traffic = traffic.received + traffic.transmitted;
let total = stored_traffic.total + current_traffic;
let data_total = Data { total };
NetworkAlertContext {
alert,
back: None,
data_total,
flash_name: None,
flash_msg: None,
threshold,
title: None,
traffic,
}
}
}
#[get("/network/wifi/usage")]
pub fn wifi_usage(flash: Option<FlashMessage>, auth: Authenticated) -> Template {
let mut context = NetworkAlertContext::build();
// set back icon link to network route
context.back = Some("/network".to_string());
context.title = Some("Network Data Usage".to_string());
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_usage", &context)
}
#[post("/network/wifi/usage", data = "<thresholds>")]
pub fn wifi_usage_alerts(thresholds: Form<Threshold>, auth: Authenticated) -> Flash<Redirect> {
match monitor::update_store(thresholds.into_inner()) {
Ok(_) => {
debug!("WiFi data usage thresholds updated.");
Flash::success(
Redirect::to("/network/wifi/usage"),
"Updated alert thresholds and flags",
)
}
Err(_) => {
warn!("Failed to update WiFi data usage thresholds.");
Flash::error(
Redirect::to("/network/wifi/usage"),
"Failed to update alert thresholds and flags",
)
}
}
}
#[post("/api/v1/network/wifi/usage", data = "<thresholds>")]
pub fn update_wifi_alerts(thresholds: Json<Threshold>, auth: Authenticated) -> Value {
match monitor::update_store(thresholds.into_inner()) {
Ok(_) => {
debug!("WiFi data usage thresholds updated.");
let status = "success".to_string();
let msg = "Updated alert threshold and flags.".to_string();
build_json_response(status, None, Some(msg))
}
Err(_) => {
warn!("Failed to update WiFi data usage thresholds.");
let status = "error".to_string();
let msg = "Failed to update WiFi data usage thresholds.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[post("/api/v1/network/wifi/usage/reset")]
pub fn reset_data_total(auth: Authenticated) -> Value {
match monitor::reset_data() {
Ok(_) => {
debug!("Reset network data usage total.");
let traffic = match network_client::traffic("wlan0") {
Ok(t) => t,
Err(_) => Traffic {
received: 0,
transmitted: 0,
rx_unit: None,
tx_unit: None,
},
};
// current wifi traffic values as bytes
let current_traffic = traffic.received + traffic.transmitted;
let data = json!(current_traffic);
let status = "success".to_string();
let msg = "Reset network data usage total.".to_string();
build_json_response(status, Some(data), Some(msg))
}
Err(_) => {
warn!("Failed to reset network data usage total.");
let status = "error".to_string();
let msg = "Failed to reset network data usage total.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
// HELPERS AND ROUTES FOR ACCESS POINT ACTIVATION
#[post("/api/v1/network/activate_ap")]
pub fn activate_ap(auth: Authenticated) -> Value {
// activate the wireless access point
debug!("Activating WiFi access point.");
match network_client::activate_ap() {
Ok(_) => {
let status = "success".to_string();
build_json_response(status, None, None)
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to activate WiFi access point.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
// HELPERS AND ROUTES FOR WIFI CLIENT MANAGEMENT
#[post("/api/v1/network/activate_client")]
pub fn activate_client(auth: Authenticated) -> Value {
// activate the wireless client
debug!("Activating WiFi client mode.");
match network_client::activate_client() {
Ok(_) => {
let status = "success".to_string();
build_json_response(status, None, None)
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to activate WiFi client mode.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[post("/api/v1/network/wifi", data = "<wifi>")]
pub fn add_wifi(wifi: Json<WiFi>, auth: Authenticated) -> Value {
// generate and write wifi config to wpa_supplicant
match network_client::add(&wifi.ssid, &wifi.pass) {
Ok(_) => {
debug!("Added WiFi credentials.");
// force reread of wpa_supplicant.conf file with new credentials
match network_client::reconfigure() {
Ok(_) => debug!("Successfully reconfigured wpa_supplicant."),
Err(_) => warn!("Failed to reconfigure wpa_supplicant."),
}
// json response for successful update
let status = "success".to_string();
let msg = "WiFi credentials added.".to_string();
build_json_response(status, None, Some(msg))
}
Err(_) => {
debug!("Failed to add WiFi credentials.");
// json response for failed update
let status = "error".to_string();
let msg = "Failed to add WiFi credentials.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[post("/api/v1/network/wifi/connect", data = "<ssid>")]
pub fn connect_ap(ssid: Json<Ssid>, auth: Authenticated) -> Value {
// retrieve the id for the given network ssid
match network_client::id("wlan0", &ssid.ssid) {
// attempt connection with the given network
Ok(id) => match network_client::connect(&id, "wlan0") {
Ok(_) => {
let status = "success".to_string();
let msg = "Connected to chosen network.".to_string();
build_json_response(status, None, Some(msg))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to connect to chosen network.".to_string();
build_json_response(status, None, Some(msg))
}
},
Err(_) => {
let status = "error".to_string();
let msg = "Failed to retrieve the network ID.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[post("/api/v1/network/wifi/disconnect", data = "<ssid>")]
pub fn disconnect_ap(ssid: Json<Ssid>, auth: Authenticated) -> Value {
// attempt to disable the current network for wlan0 interface
match network_client::disable("wlan0", &ssid.ssid) {
Ok(_) => {
let status = "success".to_string();
let msg = "Disconnected from WiFi network.".to_string();
build_json_response(status, None, Some(msg))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to disconnect from WiFi network.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[post("/api/v1/network/wifi/forget", data = "<network>")]
pub fn forget_ap(network: Json<Ssid>, auth: Authenticated) -> Value {
let ssid = &network.ssid;
match network_client::forget("wlan0", ssid) {
Ok(_) => {
debug!("Removed WiFi credentials for chosen network.");
let status = "success".to_string();
let msg = "WiFi network credentials removed.".to_string();
build_json_response(status, None, Some(msg))
}
Err(_) => {
warn!("Failed to remove WiFi credentials.");
let status = "error".to_string();
let msg = "Failed to remove WiFi network credentials.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[post("/api/v1/network/wifi/modify", data = "<wifi>")]
pub fn modify_password(wifi: Json<WiFi>, auth: Authenticated) -> Value {
let ssid = &wifi.ssid;
let pass = &wifi.pass;
// we are using a helper function (`update`) to delete the old
// credentials and add the new ones. this is because the wpa_cli method
// for updating the password does not work.
match network_client::update("wlan0", ssid, pass) {
Ok(_) => {
debug!("WiFi password updated for chosen network.");
let status = "success".to_string();
let msg = "WiFi password updated.".to_string();
build_json_response(status, None, Some(msg))
}
Err(_) => {
warn!("Failed to update WiFi password.");
let status = "error".to_string();
let msg = "Failed to update WiFi password.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
// HELPERS AND ROUTES FOR NETWORK STATE QUERIES
#[get("/api/v1/network/ip")]
pub fn return_ip(auth: Authenticated) -> Value {
// retrieve ip for wlan0 or set to x.x.x.x if not found
let wlan_ip = match network_client::ip("wlan0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
// retrieve ip for ap0 or set to x.x.x.x if not found
let ap_ip = match network_client::ip("ap0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
let data = json!({
"wlan0": wlan_ip,
"ap0": ap_ip
});
let status = "success".to_string();
build_json_response(status, Some(data), None)
}
#[get("/api/v1/network/rssi")]
pub fn return_rssi(auth: Authenticated) -> Value {
// retrieve rssi for connected network
match network_client::rssi("wlan0") {
Ok(rssi) => {
let status = "success".to_string();
let data = json!(rssi);
build_json_response(status, Some(data), None)
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[get("/api/v1/network/ssid")]
pub fn return_ssid(auth: Authenticated) -> Value {
// retrieve ssid for connected network
match network_client::ssid("wlan0") {
Ok(network) => {
let status = "success".to_string();
let data = json!(network);
build_json_response(status, Some(data), None)
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[get("/api/v1/network/state")]
pub fn return_state(auth: Authenticated) -> Value {
// retrieve state of wlan0 or set to x.x.x.x if not found
let wlan_state = match network_client::state("wlan0") {
Ok(state) => state,
Err(_) => "unavailable".to_string(),
};
// retrieve state for ap0 or set to x.x.x.x if not found
let ap_state = match network_client::state("ap0") {
Ok(state) => state,
Err(_) => "unavailable".to_string(),
};
let data = json!({
"wlan0": wlan_state,
"ap0": ap_state
});
let status = "success".to_string();
build_json_response(status, Some(data), None)
}
#[get("/api/v1/network/status")]
pub fn return_status(auth: Authenticated) -> Value {
// retrieve status info for wlan0 interface
match network_client::status("wlan0") {
Ok(network) => {
let status = "success".to_string();
let data = json!(network);
build_json_response(status, Some(data), None)
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
build_json_response(status, None, Some(msg))
}
}
}
#[get("/api/v1/network/wifi")]
pub fn scan_networks(auth: Authenticated) -> Value {
// retrieve scan results for access-points within range of wlan0
match network_client::available_networks("wlan0") {
Ok(networks) => {
let status = "success".to_string();
let data = json!(networks);
build_json_response(status, Some(data), None)
}
Err(_) => {
let status = "success".to_string();
let msg = "Unable to scan for networks. Interface may be deactivated.".to_string();
build_json_response(status, None, Some(msg))
}
}
}