diff --git a/src/client.rs b/src/client.rs index a685714..63a0a89 100644 --- a/src/client.rs +++ b/src/client.rs @@ -8,7 +8,7 @@ use serde_json::{json, Value}; use hyper::{Method, Request, body::Body, header::{CONTENT_TYPE, SET_COOKIE, AUTHORIZATION, COOKIE}, client::HttpConnector}; use log::{trace, debug}; -use crate::router_info::Status; +use crate::{status::Status, wan::WANConfiguration, devices::Device}; /// A structure to hold all the necessary data to connect to the router. pub struct Client { @@ -112,6 +112,76 @@ impl Client { return status; } + /// Get WAN configuration data from the router. + pub async fn get_wan_config(&self) -> WANConfiguration { + assert!(!self.cookies.is_empty() && self.context_id.is_some(), + "Cannot get status without logging in beforehand."); + trace!("Getting router status."); + let post_data = json!({ + "service": "NMC", + "method": "getWANStatus", + "parameters": {} + }); + + let req = Request::builder() + .method(Method::POST) + .uri(format!("http://{}/ws", self.ip)) + .header(CONTENT_TYPE, "application/x-sah-ws-4-call+json") + .header("X-Context", self.context_id.clone().unwrap()) + .header(COOKIE, self.cookies.join("; ")) + .body(Body::from(post_data.to_string())) + .expect("Could not build request."); + let (parts, body) = self.client.request(req).await + .expect("There was an issue contacting the router.").into_parts(); + + debug!("Status is {}.", parts.status.as_str()); + let body_bytes = hyper::body::to_bytes(body).await.unwrap(); + let json: Value = serde_json::from_slice(&body_bytes) + .expect("Could not parse JSON."); + debug!("Body was: '{}'.", std::str::from_utf8(&body_bytes).unwrap()); + assert!(parts.status.is_success() && json["status"].as_bool().unwrap(), + "Router answered with something else than a success code."); + let wan_config: WANConfiguration = serde_json::from_value(json["data"].clone()) + .expect("Looks like the deserialized data is incomplete."); + debug!("Deserialized settings is: {:?}", wan_config); + return wan_config; + } + + /// Get various settings data from the router. + pub async fn get_devices(&self) -> Vec { + assert!(!self.cookies.is_empty() && self.context_id.is_some(), + "Cannot get status without logging in beforehand."); + trace!("Getting router status."); + let post_data = json!({ + "service": "Devices", + "method": "get", + "parameters": {} + }); + + let req = Request::builder() + .method(Method::POST) + .uri(format!("http://{}/ws", self.ip)) + .header(CONTENT_TYPE, "application/x-sah-ws-4-call+json") + .header("X-Context", self.context_id.clone().unwrap()) + .header(COOKIE, self.cookies.join("; ")) + .body(Body::from(post_data.to_string())) + .expect("Could not build request."); + let (parts, body) = self.client.request(req).await + .expect("There was an issue contacting the router.").into_parts(); + + debug!("Status is {}.", parts.status.as_str()); + let body_bytes = hyper::body::to_bytes(body).await.unwrap(); + let json: Value = serde_json::from_slice(&body_bytes) + .expect("Could not parse JSON."); + debug!("Body was: '{}'.", std::str::from_utf8(&body_bytes).unwrap()); + assert!(parts.status.is_success() && json["status"].is_array(), + "Router answered with something else than a success code."); + let devices: Vec = serde_json::from_value(json["status"].clone()) + .expect("Looks like the deserialized data is incomplete."); + debug!("Deserialized settings is: {:?}", devices); + return devices; + } + /// Logout from the router. pub async fn logout(&mut self) { trace!("Logging out."); diff --git a/src/devices.rs b/src/devices.rs new file mode 100644 index 0000000..071f702 --- /dev/null +++ b/src/devices.rs @@ -0,0 +1,22 @@ + +///! All the necessary struct elements to store the router devices information. + +use serde::Deserialize; + +/// The Device structure is used to store status data from the devices that are +/// or have been connected to the router at some point. +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[serde(rename_all="PascalCase")] +pub struct Device { + pub key: String, + pub name: String, + pub discovery_source: String, + pub active: bool, + pub device_type: String, + pub tags: String, + #[serde(rename(deserialize = "IPAddress"))] + pub ip_address: Option, + #[serde(rename(deserialize = "SSID"))] + pub ssid: Option, + pub channel: Option, +} diff --git a/src/lib.rs b/src/lib.rs index 530ea06..2860b78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,4 +2,6 @@ //! A crate aimed to gather (read only) data from a Livebox 4 or more recent. pub mod client; -pub mod router_info; +pub mod status; +pub mod wan; +pub mod devices; diff --git a/src/router_info.rs b/src/status.rs similarity index 50% rename from src/router_info.rs rename to src/status.rs index bd6a1ee..ba4cd23 100644 --- a/src/router_info.rs +++ b/src/status.rs @@ -6,60 +6,37 @@ use serde::Deserialize; /// The Status structure is used to store status data from the router. #[derive(Debug, Deserialize, Clone, PartialEq, Eq)] #[serde(deny_unknown_fields)] +#[serde(rename_all="PascalCase")] pub struct Status { - #[serde(rename(deserialize = "Manufacturer"))] pub manufacturer: String, #[serde(rename(deserialize = "ManufacturerOUI"))] pub manufacturer_oui: String, - #[serde(rename(deserialize = "ModelName"))] pub model_name: String, - #[serde(rename(deserialize = "Description"))] pub description: String, - #[serde(rename(deserialize = "ProductClass"))] pub product_class: String, - #[serde(rename(deserialize = "SerialNumber"))] pub serial_number: String, - #[serde(rename(deserialize = "HardwareVersion"))] pub hardware_version: String, - #[serde(rename(deserialize = "SoftwareVersion"))] pub software_version: String, - #[serde(rename(deserialize = "RescueVersion"))] pub rescue_version: String, - #[serde(rename(deserialize = "ModemFirmwareVersion"))] pub modem_firmware_version: String, - #[serde(rename(deserialize = "EnabledOptions"))] pub enabled_options: String, - #[serde(rename(deserialize = "AdditionalHardwareVersion"))] pub additional_hardware_version: String, - #[serde(rename(deserialize = "AdditionalSoftwareVersion"))] pub additional_software_version: String, - #[serde(rename(deserialize = "SpecVersion"))] pub spec_version: String, - #[serde(rename(deserialize = "ProvisioningCode"))] pub provisioning_code: String, - #[serde(rename(deserialize = "UpTime"))] - pub uptime: u32, - #[serde(rename(deserialize = "FirstUseDate"))] + pub up_time: u32, pub first_use_date: String, - #[serde(rename(deserialize = "DeviceLog"))] pub device_log: String, - #[serde(rename(deserialize = "VendorConfigFileNumberOfEntries"))] - pub vendor_configfile_number_of_entries: u32, + pub vendor_config_file_number_of_entries: u32, #[serde(rename(deserialize = "ManufacturerURL"))] pub manufacturer_url: String, - #[serde(rename(deserialize = "Country"))] pub country: String, #[serde(rename(deserialize = "ExternalIPAddress"))] pub external_ip_address: String, - #[serde(rename(deserialize = "DeviceStatus"))] pub device_status: String, - #[serde(rename(deserialize = "NumberOfReboots"))] pub number_of_reboots: u32, - #[serde(rename(deserialize = "UpgradeOccurred"))] - pub upgrade_occured: bool, - #[serde(rename(deserialize = "ResetOccurred"))] + pub upgrade_occurred: bool, pub reset_occurred: bool, - #[serde(rename(deserialize = "RestoreOccurred"))] pub restore_occurred: bool, #[serde(rename(deserialize = "X_SOFTATHOME-COM_AdditionalSoftwareVersions"))] pub softathome_additional_software_versions: String, diff --git a/src/wan.rs b/src/wan.rs new file mode 100644 index 0000000..a745c1c --- /dev/null +++ b/src/wan.rs @@ -0,0 +1,28 @@ + +///! The WAN configuration that you can read from the router. + +use serde::Deserialize; + +/// The WANConfiguration structure is used to store WAN configuration data from +/// the router. +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[serde(rename_all="PascalCase")] +pub struct WANConfiguration { + pub wan_state: String, + pub link_type: String, + pub link_state: String, + #[serde(rename(deserialize = "MACAddress"))] + pub mac_address: String, + pub protocol: String, + pub connection_state: String, + pub last_connection_error: String, + #[serde(rename(deserialize = "IPAddress"))] + pub ip_address: String, + pub remote_gateway: String, + #[serde(rename(deserialize = "DNSServers"))] + pub dns_servers: String, + #[serde(rename(deserialize = "IPv6Address"))] + pub ipv6_address: String, + #[serde(rename(deserialize = "IPv6DelegatedPrefix"))] + pub ipv6_delegated_prefix: String +}