2014-11-06 15:23:02 -05:00
|
|
|
//! JSON configuration files using libserialize.
|
2014-12-23 12:53:30 -05:00
|
|
|
use std::borrow::ToOwned;
|
2014-11-02 18:16:49 -05:00
|
|
|
use std::collections::HashMap;
|
2015-03-02 17:12:16 -05:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::prelude::*;
|
|
|
|
use std::io::{Error, ErrorKind, Result};
|
2015-03-07 12:15:22 -05:00
|
|
|
use std::path::Path;
|
2015-09-16 12:21:58 -04:00
|
|
|
use rustc_serialize::json::{decode, encode};
|
2014-11-02 18:16:49 -05:00
|
|
|
|
2014-11-06 15:23:02 -05:00
|
|
|
/// Configuration data.
|
2015-09-13 13:49:15 +01:00
|
|
|
#[derive(Clone, RustcDecodable, RustcEncodable, Default, PartialEq, Debug)]
|
2014-11-02 18:16:49 -05:00
|
|
|
pub struct Config {
|
2016-01-15 12:37:16 -05:00
|
|
|
/// A list of the owners of the client by nickname (for bots).
|
2014-12-04 21:04:22 -05:00
|
|
|
pub owners: Option<Vec<String>>,
|
2016-01-15 12:37:16 -05:00
|
|
|
/// The client's nickname.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub nickname: Option<String>,
|
2016-01-15 12:37:16 -05:00
|
|
|
/// The client's NICKSERV password.
|
2014-12-09 17:01:47 -05:00
|
|
|
pub nick_password: Option<String>,
|
2016-01-15 12:37:16 -05:00
|
|
|
/// Alternative nicknames for the client, if the default is taken.
|
2014-12-06 18:49:07 -05:00
|
|
|
pub alt_nicks: Option<Vec<String>>,
|
2016-01-15 12:37:16 -05:00
|
|
|
/// The client's username.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub username: Option<String>,
|
2016-01-15 12:37:16 -05:00
|
|
|
/// The client's real name.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub realname: Option<String>,
|
2014-11-06 15:23:02 -05:00
|
|
|
/// The server to connect to.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub server: Option<String>,
|
2014-11-06 15:23:02 -05:00
|
|
|
/// The port to connect on.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub port: Option<u16>,
|
2014-12-09 16:17:05 -05:00
|
|
|
/// The password to connect to the server.
|
|
|
|
pub password: Option<String>,
|
2014-11-08 17:35:19 -05:00
|
|
|
/// Whether or not to use SSL.
|
2016-01-15 12:37:16 -05:00
|
|
|
/// Clients will automatically panic if this is enabled without SSL support.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub use_ssl: Option<bool>,
|
2014-11-30 01:29:38 -05:00
|
|
|
/// The encoding type used for this connection.
|
|
|
|
/// This is typically UTF-8, but could be something else.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub encoding: Option<String>,
|
2014-11-06 15:23:02 -05:00
|
|
|
/// A list of channels to join on connection.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub channels: Option<Vec<String>>,
|
2015-02-17 13:20:59 -05:00
|
|
|
/// User modes to set on connect. Example: "+RB-x"
|
|
|
|
pub umodes: Option<String>,
|
2014-12-22 16:35:56 -05:00
|
|
|
/// The text that'll be sent in response to CTCP USERINFO requests.
|
|
|
|
pub user_info: Option<String>,
|
2016-01-15 12:37:16 -05:00
|
|
|
/// The amount of inactivity in seconds before the client will ping the server.
|
|
|
|
pub ping_time: Option<u32>,
|
|
|
|
/// The amount of time in seconds for a client to reconnect due to no ping response.
|
|
|
|
pub ping_timeout: Option<u32>,
|
2016-02-09 18:24:52 +08:00
|
|
|
/// Whether the client should use NickServ GHOST to reclaim its primary nickname if it is in use.
|
|
|
|
/// This has no effect if `nick_password` is not set.
|
|
|
|
pub should_ghost: Option<bool>,
|
|
|
|
/// The command(s) that should be sent to NickServ to recover a nickname. The nickname and password will be appended in that order after the command.
|
|
|
|
/// E.g. `["RECOVER", "RELEASE"]` means `RECOVER nick pass` and `RELEASE nick pass` will be sent in that order.
|
|
|
|
pub ghost_sequence: Option<Vec<String>>,
|
2014-11-06 15:23:02 -05:00
|
|
|
/// A map of additional options to be stored in config.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub options: Option<HashMap<String, String>>,
|
2014-11-02 18:16:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
2014-11-06 15:23:02 -05:00
|
|
|
/// Loads a JSON configuration from the desired path.
|
2015-06-05 21:37:21 -04:00
|
|
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Config> {
|
2015-03-07 15:01:34 -05:00
|
|
|
let mut file = try!(File::open(path));
|
2015-03-02 17:12:16 -05:00
|
|
|
let mut data = String::new();
|
|
|
|
try!(file.read_to_string(&mut data));
|
2015-06-22 12:03:57 -04:00
|
|
|
decode(&data[..]).map_err(|_|
|
2015-04-03 00:56:42 -04:00
|
|
|
Error::new(ErrorKind::InvalidInput, "Failed to decode configuration file.")
|
2015-03-02 17:12:16 -05:00
|
|
|
)
|
2014-11-02 18:16:49 -05:00
|
|
|
}
|
|
|
|
|
2015-09-16 12:21:58 -04:00
|
|
|
/// Saves a JSON configuration to the desired path.
|
|
|
|
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
|
|
|
let mut file = try!(File::create(path));
|
|
|
|
file.write_all(try!(encode(self).map_err(|_|
|
|
|
|
Error::new(ErrorKind::InvalidInput, "Failed to encode configuration file.")
|
|
|
|
)).as_bytes())
|
|
|
|
}
|
|
|
|
|
2014-11-06 15:23:02 -05:00
|
|
|
/// Determines whether or not the nickname provided is the owner of the bot.
|
2014-11-02 18:16:49 -05:00
|
|
|
pub fn is_owner(&self, nickname: &str) -> bool {
|
2015-04-04 23:06:13 -04:00
|
|
|
self.owners.as_ref().map(|o| o.contains(&nickname.to_string())).unwrap()
|
2014-12-04 21:04:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the nickname specified in the configuration.
|
2014-12-06 18:49:07 -05:00
|
|
|
/// This will panic if not specified.
|
2014-12-04 21:04:22 -05:00
|
|
|
pub fn nickname(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.nickname.as_ref().map(|s| &s[..]).unwrap()
|
2014-12-04 21:04:22 -05:00
|
|
|
}
|
|
|
|
|
2014-12-09 17:01:47 -05:00
|
|
|
/// Gets the bot's nickserv password specified in the configuration.
|
|
|
|
/// This defaults to an empty string when not specified.
|
|
|
|
pub fn nick_password(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.nick_password.as_ref().map(|s| &s[..]).unwrap_or("")
|
2014-12-09 17:01:47 -05:00
|
|
|
}
|
|
|
|
|
2014-12-06 18:49:07 -05:00
|
|
|
/// Gets the alternate nicknames specified in the configuration.
|
|
|
|
/// This defaults to an empty vector when not specified.
|
2016-01-30 11:59:57 -05:00
|
|
|
pub fn alternate_nicknames(&self) -> Vec<&str> {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.alt_nicks.as_ref().map(|v| v.iter().map(|s| &s[..]).collect()).unwrap_or(vec![])
|
2014-12-06 18:49:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-04 21:04:22 -05:00
|
|
|
/// Gets the username specified in the configuration.
|
|
|
|
/// This defaults to the user's nickname when not specified.
|
|
|
|
pub fn username(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.username.as_ref().map(|s| &s[..]).unwrap_or(self.nickname())
|
2014-12-04 21:04:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the real name specified in the configuration.
|
|
|
|
/// This defaults to the user's nickname when not specified.
|
|
|
|
pub fn real_name(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.realname.as_ref().map(|s| &s[..]).unwrap_or(self.nickname())
|
2014-12-04 21:04:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the address of the server specified in the configuration.
|
|
|
|
/// This panics when not specified.
|
|
|
|
pub fn server(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.server.as_ref().map(|s| &s[..]).unwrap()
|
2014-12-04 21:04:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the port of the server specified in the configuration.
|
|
|
|
/// This defaults to 6667 (or 6697 if use_ssl is specified as true) when not specified.
|
|
|
|
pub fn port(&self) -> u16 {
|
|
|
|
self.port.as_ref().map(|p| *p).unwrap_or(if self.use_ssl() {
|
|
|
|
6697
|
|
|
|
} else {
|
|
|
|
6667
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-12-09 16:17:05 -05:00
|
|
|
/// Gets the server password specified in the configuration.
|
|
|
|
/// This defaults to a blank string when not specified.
|
|
|
|
pub fn password(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.password.as_ref().map(|s| &s[..]).unwrap_or("")
|
2014-12-09 16:17:05 -05:00
|
|
|
}
|
|
|
|
|
2014-12-04 21:04:22 -05:00
|
|
|
/// Gets whether or not to use SSL with this connection.
|
|
|
|
/// This defaults to false when not specified.
|
|
|
|
pub fn use_ssl(&self) -> bool {
|
|
|
|
self.use_ssl.as_ref().map(|u| *u).unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the encoding to use for this connection. This requires the encode feature to work.
|
|
|
|
/// This defaults to UTF-8 when not specified.
|
|
|
|
pub fn encoding(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.encoding.as_ref().map(|s| &s[..]).unwrap_or("UTF-8")
|
2014-12-04 21:04:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the channels to join upon connection.
|
|
|
|
/// This defaults to an empty vector if it's not specified.
|
|
|
|
pub fn channels(&self) -> Vec<&str> {
|
2015-06-22 12:03:57 -04:00
|
|
|
self.channels.as_ref().map(|v| v.iter().map(|s| &s[..]).collect()).unwrap_or(vec![])
|
2014-11-02 18:16:49 -05:00
|
|
|
}
|
2014-11-29 05:49:20 -05:00
|
|
|
|
2015-02-17 13:20:59 -05:00
|
|
|
/// Gets the user modes to set on connect specified in the configuration.
|
|
|
|
/// This defaults to an empty string when not specified.
|
|
|
|
pub fn umodes(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.umodes.as_ref().map(|s| &s[..]).unwrap_or("")
|
2015-02-17 13:20:59 -05:00
|
|
|
}
|
|
|
|
|
2014-12-22 16:35:56 -05:00
|
|
|
/// Gets the string to be sent in response to CTCP USERINFO requests.
|
|
|
|
/// This defaults to an empty string when not specified.
|
|
|
|
pub fn user_info(&self) -> &str {
|
2015-02-21 09:28:12 -05:00
|
|
|
self.user_info.as_ref().map(|s| &s[..]).unwrap_or("")
|
2014-12-22 16:35:56 -05:00
|
|
|
}
|
|
|
|
|
2016-01-15 12:37:16 -05:00
|
|
|
/// Gets the amount of time in seconds since last activity necessary for the client to ping the
|
|
|
|
/// server.
|
|
|
|
/// This defaults to 180 seconds when not specified.
|
|
|
|
pub fn ping_time(&self) -> u32 {
|
|
|
|
self.ping_time.as_ref().map(|t| *t).unwrap_or(180)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the amount of time in seconds for the client to reconnect after no ping response.
|
|
|
|
/// This defaults to 10 seconds when not specified.
|
|
|
|
pub fn ping_timeout(&self) -> u32 {
|
|
|
|
self.ping_timeout.as_ref().map(|t| *t).unwrap_or(10)
|
|
|
|
}
|
|
|
|
|
2016-02-09 18:24:52 +08:00
|
|
|
/// Gets whether or not to use NickServ GHOST
|
|
|
|
/// This defaults to false when not specified.
|
|
|
|
pub fn should_ghost(&self) -> bool {
|
|
|
|
self.should_ghost.as_ref().map(|u| *u).unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the NickServ command sequence to recover a nickname.
|
|
|
|
/// This defaults to `["GHOST"]` when not specified.
|
|
|
|
pub fn ghost_sequence(&self) -> Vec<&str> {
|
|
|
|
self.ghost_sequence.as_ref().map(|v| v.iter().map(|s| &s[..]).collect()).unwrap_or(vec!["GHOST"])
|
|
|
|
}
|
|
|
|
|
2014-11-29 05:49:20 -05:00
|
|
|
/// Looks up the specified string in the options map.
|
|
|
|
/// This uses indexing, and thus panics when the string is not present.
|
2014-12-04 21:04:22 -05:00
|
|
|
/// This will also panic if used and there are no options.
|
2014-11-29 05:49:20 -05:00
|
|
|
pub fn get_option(&self, option: &str) -> &str {
|
2015-03-26 21:50:38 -04:00
|
|
|
self.options.as_ref().map(|o| &o[&option.to_owned()][..]).unwrap()
|
2014-11-29 05:49:20 -05:00
|
|
|
}
|
2014-11-02 18:16:49 -05:00
|
|
|
}
|
2014-11-05 01:45:17 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::Config;
|
|
|
|
use std::collections::HashMap;
|
2014-12-05 10:27:58 -05:00
|
|
|
use std::default::Default;
|
2015-03-07 15:01:34 -05:00
|
|
|
use std::path::Path;
|
2014-11-05 01:45:17 -05:00
|
|
|
|
|
|
|
#[test]
|
2014-11-30 03:30:10 -05:00
|
|
|
fn load() {
|
|
|
|
let cfg = Config {
|
2014-12-04 21:04:22 -05:00
|
|
|
owners: Some(vec![format!("test")]),
|
|
|
|
nickname: Some(format!("test")),
|
2014-12-09 17:01:47 -05:00
|
|
|
nick_password: None,
|
2014-12-06 18:49:07 -05:00
|
|
|
alt_nicks: None,
|
2014-12-04 21:04:22 -05:00
|
|
|
username: Some(format!("test")),
|
|
|
|
realname: Some(format!("test")),
|
|
|
|
password: Some(String::new()),
|
2015-02-17 17:56:06 +00:00
|
|
|
umodes: Some(format!("+BR")),
|
2014-12-04 21:04:22 -05:00
|
|
|
server: Some(format!("irc.test.net")),
|
|
|
|
port: Some(6667),
|
|
|
|
use_ssl: Some(false),
|
|
|
|
encoding: Some(format!("UTF-8")),
|
|
|
|
channels: Some(vec![format!("#test"), format!("#test2")]),
|
2014-12-23 12:15:41 -05:00
|
|
|
user_info: None,
|
2016-01-16 11:47:25 -05:00
|
|
|
ping_time: None,
|
|
|
|
ping_timeout: None,
|
2016-02-09 18:24:52 +08:00
|
|
|
should_ghost: None,
|
|
|
|
ghost_sequence: None,
|
2014-12-04 21:04:22 -05:00
|
|
|
options: Some(HashMap::new()),
|
2014-11-30 03:30:10 -05:00
|
|
|
};
|
2015-04-03 00:56:42 -04:00
|
|
|
assert_eq!(Config::load(Path::new("client_config.json")).unwrap(), cfg);
|
2014-11-30 03:30:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2015-06-05 21:37:21 -04:00
|
|
|
fn load_from_str() {
|
2014-11-30 03:30:10 -05:00
|
|
|
let cfg = Config {
|
2014-12-04 21:04:22 -05:00
|
|
|
owners: Some(vec![format!("test")]),
|
|
|
|
nickname: Some(format!("test")),
|
2014-12-09 17:01:47 -05:00
|
|
|
nick_password: None,
|
2014-12-06 18:49:07 -05:00
|
|
|
alt_nicks: None,
|
2014-12-04 21:04:22 -05:00
|
|
|
username: Some(format!("test")),
|
|
|
|
realname: Some(format!("test")),
|
2015-02-17 17:56:06 +00:00
|
|
|
umodes: Some(format!("+BR")),
|
2014-12-04 21:04:22 -05:00
|
|
|
password: Some(String::new()),
|
|
|
|
server: Some(format!("irc.test.net")),
|
|
|
|
port: Some(6667),
|
|
|
|
use_ssl: Some(false),
|
|
|
|
encoding: Some(format!("UTF-8")),
|
|
|
|
channels: Some(vec![format!("#test"), format!("#test2")]),
|
2014-12-23 12:15:41 -05:00
|
|
|
user_info: None,
|
2016-01-16 11:47:25 -05:00
|
|
|
ping_time: None,
|
|
|
|
ping_timeout: None,
|
2016-02-09 18:24:52 +08:00
|
|
|
should_ghost: None,
|
|
|
|
ghost_sequence: None,
|
2014-12-04 21:04:22 -05:00
|
|
|
options: Some(HashMap::new()),
|
2014-11-30 03:30:10 -05:00
|
|
|
};
|
2015-06-05 21:37:21 -04:00
|
|
|
assert_eq!(Config::load("client_config.json").unwrap(), cfg);
|
2014-11-30 03:30:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
2014-11-05 01:45:17 -05:00
|
|
|
fn is_owner() {
|
|
|
|
let cfg = Config {
|
2014-12-04 21:04:22 -05:00
|
|
|
owners: Some(vec![format!("test"), format!("test2")]),
|
2014-12-05 10:27:58 -05:00
|
|
|
.. Default::default()
|
2014-11-05 01:45:17 -05:00
|
|
|
};
|
|
|
|
assert!(cfg.is_owner("test"));
|
|
|
|
assert!(cfg.is_owner("test2"));
|
|
|
|
assert!(!cfg.is_owner("test3"));
|
|
|
|
}
|
2014-11-29 05:49:20 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn get_option() {
|
2014-12-04 21:04:22 -05:00
|
|
|
let cfg = Config {
|
2014-11-30 03:30:10 -05:00
|
|
|
options: {
|
|
|
|
let mut map = HashMap::new();
|
|
|
|
map.insert(format!("testing"), format!("test"));
|
2014-12-04 21:04:22 -05:00
|
|
|
Some(map)
|
2014-11-30 03:30:10 -05:00
|
|
|
},
|
2014-12-05 10:27:58 -05:00
|
|
|
.. Default::default()
|
2014-11-30 03:30:10 -05:00
|
|
|
};
|
|
|
|
assert_eq!(cfg.get_option("testing"), "test");
|
|
|
|
}
|
2014-11-05 01:45:17 -05:00
|
|
|
}
|