From 39232298344f2fdc9bb6c9abe6dc0ffa0dc578c9 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Fri, 23 Sep 2016 00:41:49 -0700 Subject: [PATCH] Added DDNS broadcasting --- Cargo.lock | 1 + Cargo.toml | 1 + Polaris.toml | 7 ++++- src/collection.rs | 11 +++++-- src/config.rs | 41 +++++++++++++++++++++++-- src/ddns.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 16 ++++++++++ 7 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 src/ddns.rs diff --git a/Cargo.lock b/Cargo.lock index eb61873..7eda0e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,7 @@ name = "polaris" version = "0.1.0" dependencies = [ + "hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", "id3 0.1.10 (git+https://github.com/jameshurst/rust-id3)", "iron 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (git+https://github.com/retep998/winapi-rs)", diff --git a/Cargo.toml b/Cargo.toml index 98e88dd..d2c5c97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Antoine Gersant "] ui = [] [dependencies] +hyper = "0.9.10" id3 = { git = "https://github.com/jameshurst/rust-id3" } iron = "0.4.0" mount = "0.2.1" diff --git a/Polaris.toml b/Polaris.toml index 0276734..064dbc4 100644 --- a/Polaris.toml +++ b/Polaris.toml @@ -6,4 +6,9 @@ source = 'M:/Music/Genres/' [[users]] name = 'agersant' -password = 'test' \ No newline at end of file +password = 'test' + +[ydns] +host = 'your_hostname.ydns.eu' +username = 'your_username' +password = 'your_ydns_password' diff --git a/src/collection.rs b/src/collection.rs index 7decdbc..e7eeeb5 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -202,7 +202,7 @@ pub enum CollectionFile { pub struct Collection { vfs: Vfs, users: Vec, - album_art_pattern: Regex, + album_art_pattern: Option, } impl Collection { @@ -210,7 +210,7 @@ impl Collection { Collection { vfs: Vfs::new(), users: Vec::new(), - album_art_pattern: Regex::new("^Folder\\.png$").unwrap(), + album_art_pattern: None, } } @@ -294,6 +294,11 @@ impl Collection { } fn get_album_art(&self, real_path: &Path) -> Result, PError> { + let pattern = match self.album_art_pattern { + Some(ref p) => p, + None => return Ok(None), + }; + let mut real_dir = real_path; if real_dir.is_file() { real_dir = try!(real_dir.parent().ok_or(PError::AlbumArtSearchError)); @@ -311,7 +316,7 @@ impl Collection { None => return false, Some(r) => r, }; - self.album_art_pattern.is_match(file_name) + pattern.is_match(file_name) }); match album_art { diff --git a/src/config.rs b/src/config.rs index afb6e31..44741db 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,6 +6,7 @@ use std::path; use toml; use collection::User; +use ddns::DDNSConfig; use vfs::MountDir; const CONFIG_MOUNT_DIRS: &'static str = "mount_dirs"; @@ -15,6 +16,10 @@ const CONFIG_USERS: &'static str = "users"; const CONFIG_USER_NAME: &'static str = "name"; const CONFIG_USER_PASSWORD: &'static str = "password"; const CONFIG_ALBUM_ART_PATTERN: &'static str = "album_art_pattern"; +const CONFIG_DDNS: &'static str = "ydns"; +const CONFIG_DDNS_HOST: &'static str = "host"; +const CONFIG_DDNS_USERNAME: &'static str = "username"; +const CONFIG_DDNS_PASSWORD: &'static str = "password"; #[derive(Debug)] pub enum ConfigError { @@ -24,6 +29,7 @@ pub enum ConfigError { AlbumArtPatternParseError, UsersParseError, MountDirsParseError, + DDNSParseError, } impl From for ConfigError { @@ -41,7 +47,8 @@ impl From for ConfigError { pub struct Config { pub mount_dirs: Vec, pub users: Vec, - pub album_art_pattern: regex::Regex, + pub album_art_pattern: Option, + pub ddns: Option, } impl Config { @@ -55,12 +62,14 @@ impl Config { let mut config = Config { mount_dirs: Vec::new(), users: Vec::new(), - album_art_pattern: regex::Regex::new("^Folder\\.png$").unwrap(), + album_art_pattern: None, + ddns: None, }; try!(config.parse_mount_points(&parsed_config)); try!(config.parse_users(&parsed_config)); try!(config.parse_album_art_pattern(&parsed_config)); + try!(config.parse_ddns(&parsed_config)); Ok(config) } @@ -74,7 +83,7 @@ impl Config { &toml::Value::String(ref s) => s, _ => return Err(ConfigError::AlbumArtPatternParseError), }; - self.album_art_pattern = try!(regex::Regex::new(pattern)); + self.album_art_pattern = Some(try!(regex::Regex::new(pattern))); Ok(()) } @@ -152,4 +161,30 @@ impl Config { Ok(()) } + + fn parse_ddns(&mut self, source: &toml::Table) -> Result<(), ConfigError> { + let ddns = match source.get(CONFIG_DDNS) { + Some(s) => s, + None => return Ok(()), + }; + let ddns = match ddns { + &toml::Value::Table(ref a) => a, + _ => return Err(ConfigError::DDNSParseError), + }; + + let host = try!(ddns.get(CONFIG_DDNS_HOST).ok_or(ConfigError::DDNSParseError)).as_str(); + let username = try!(ddns.get(CONFIG_DDNS_USERNAME).ok_or(ConfigError::DDNSParseError)).as_str(); + let password = try!(ddns.get(CONFIG_DDNS_PASSWORD).ok_or(ConfigError::DDNSParseError)).as_str(); + + let host = try!(host.ok_or(ConfigError::DDNSParseError)); + let username = try!(username.ok_or(ConfigError::DDNSParseError)); + let password = try!(password.ok_or(ConfigError::DDNSParseError)); + + self.ddns = Some(DDNSConfig { + host: host.to_owned(), + username: username.to_owned(), + password: password.to_owned(), + }); + Ok(()) + } } \ No newline at end of file diff --git a/src/ddns.rs b/src/ddns.rs new file mode 100644 index 0000000..f0f0a43 --- /dev/null +++ b/src/ddns.rs @@ -0,0 +1,78 @@ +use hyper; +use hyper::client::Client; +use hyper::header::{Authorization, Basic }; +use std::io; +use std::io::Read; +use std::thread; +use std::time; + +#[derive(Debug, Clone)] +pub struct DDNSConfig { + pub host: String, + pub username: String, + pub password: String, +} + +#[derive(Debug)] +enum DDNSError { + IoError(io::Error), + HyperError(hyper::Error), + UpdateError(hyper::status::StatusCode), +} + +impl From for DDNSError { + fn from(err: io::Error) -> DDNSError { + DDNSError::IoError(err) + } +} + +impl From for DDNSError { + fn from(err: hyper::Error) -> DDNSError { + DDNSError::HyperError(err) + } +} + +const MY_IP_API_URL: &'static str = "http://api.ipify.org"; +const DDNS_UPDATE_URL: &'static str = "http://ydns.io/api/v1/update/"; + +fn get_my_ip() -> Result { + let client = Client::new(); + let mut res = try!(client.get(MY_IP_API_URL).send()); + let mut buf = String::new(); + try!(res.read_to_string(&mut buf)); + Ok(buf) +} + +fn update_my_ip(ip: &String, config: &DDNSConfig) -> Result<(), DDNSError> { + let client = Client::new(); + let url = DDNS_UPDATE_URL; + let host = &config.host; + let full_url = format!("{}?host={}&ip={}", url, host, ip); + let auth_header = Authorization(Basic { + username: config.username.clone(), + password: Some(config.password.to_owned()) + }); + + let res = try!(client.get(full_url.as_str()).header(auth_header).send()); + match res.status { + hyper::status::StatusCode::Ok => Ok(()), + s => Err(DDNSError::UpdateError(s)), + } +} + +pub fn run(config: DDNSConfig) { + loop { + let my_ip_res = get_my_ip(); + if let Ok(my_ip) = my_ip_res { + match update_my_ip(&my_ip, &config) { + Err(e) => println!("Dynamic DNS Error: {:?}", e), + Ok(_) => (), + }; + } + else + { + println!("Dynamic DNS Error: could not retrieve our own IP address"); + } + thread::sleep(time::Duration::from_secs(60 * 30)); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c2f1c40..45031bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ extern crate core; +extern crate hyper; extern crate id3; extern crate iron; extern crate mount; @@ -32,15 +33,18 @@ use staticfile::Static; mod api; mod collection; mod config; +mod ddns; mod error; mod ui; mod vfs; fn main() { + // Parse config let config_file = Path::new("Polaris.toml"); let config = config::Config::parse(&config_file).unwrap(); + // Start server println!("Starting up server"); let mut api_chain; { @@ -64,6 +68,18 @@ fn main() { mount.mount("/", Static::new(Path::new("web"))); let mut server = Iron::new(mount).http(("0.0.0.0", 5050)).unwrap(); + // Start DDNS updates + match config.ddns { + Some(ref ddns_config) => { + let ddns_config = ddns_config.clone(); + std::thread::spawn(|| { + ddns::run(ddns_config); + }); + }, + None => (), + }; + + // Run UI ui::run(); println!("Shutting down server");