Implemented password support for nickservs as per #9.

This commit is contained in:
Aaron Weiss 2014-12-09 17:01:47 -05:00
parent 31281d2820
commit 398cda4af6
3 changed files with 147 additions and 6 deletions

View file

@ -4,9 +4,10 @@ use std::io::{InvalidInput, IoError, IoResult};
use std::str::FromStr;
use data::message::Message;
/// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812).
/// This also includes commands from the
/// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812). This
/// also includes commands from the
/// [capabilities extension](https://tools.ietf.org/html/draft-mitchell-irc-capabilities-01).
/// Additionally, this includes some common additional commands from popular IRCds.
#[stable]
#[deriving(Show, PartialEq)]
pub enum Command<'a> {
@ -131,6 +132,18 @@ pub enum Command<'a> {
SAPART(&'a str, &'a str),
/// SAQUIT nickname reason
SAQUIT(&'a str, &'a str),
/// NICKSERV :message
NICKSERV(&'a str),
/// CHANSERV :message
CHANSERV(&'a str),
/// OPERSERV :message
OPERSERV(&'a str),
/// BOTSERV :message
BOTSERV(&'a str),
/// HOSTSERV :message
HOSTSERV(&'a str),
/// MEMOSERV :message
MEMOSERV(&'a str),
// Capabilities extension to IRCv3
/// CAP COMMAND [param]
@ -239,7 +252,12 @@ impl<'a> Command<'a> {
Command::SANICK(o, n) => Message::new(None, "SANICK", Some(vec![o, n]), None),
Command::SAPART(c, r) => Message::new(None, "SAPART", Some(vec![c]), Some(r)),
Command::SAQUIT(c, r) => Message::new(None, "SAQUIT", Some(vec![c]), Some(r)),
Command::NICKSERV(m) => Message::new(None, "NICKSERV", None, Some(m)),
Command::CHANSERV(m) => Message::new(None, "CHANSERV", None, Some(m)),
Command::OPERSERV(m) => Message::new(None, "OPERSERV", None, Some(m)),
Command::BOTSERV(m) => Message::new(None, "BOTSERV", None, Some(m)),
Command::HOSTSERV(m) => Message::new(None, "HOSTSERV", None, Some(m)),
Command::MEMOSERV(m) => Message::new(None, "MEMOSERV", None, Some(m)),
Command::CAP(s, p) => Message::new(None, "CAP", Some(vec![s.to_str()]), p),
}
}
@ -846,6 +864,72 @@ impl<'a> Command<'a> {
Command::SAQUIT(m.args[0][], m.args[1][])
}
}
} else if let "NICKSERV" = m.command[] {
match m.suffix {
Some(ref suffix) => {
if m.args.len() != 0 { return Err(invalid_input()) }
Command::NICKSERV(suffix[])
},
None => {
if m.args.len() != 1 { return Err(invalid_input()) }
Command::NICKSERV(m.args[0][])
}
}
} else if let "CHANSERV" = m.command[] {
match m.suffix {
Some(ref suffix) => {
if m.args.len() != 0 { return Err(invalid_input()) }
Command::CHANSERV(suffix[])
},
None => {
if m.args.len() != 1 { return Err(invalid_input()) }
Command::CHANSERV(m.args[0][])
}
}
} else if let "OPERSERV" = m.command[] {
match m.suffix {
Some(ref suffix) => {
if m.args.len() != 0 { return Err(invalid_input()) }
Command::OPERSERV(suffix[])
},
None => {
if m.args.len() != 1 { return Err(invalid_input()) }
Command::OPERSERV(m.args[0][])
}
}
} else if let "BOTSERV" = m.command[] {
match m.suffix {
Some(ref suffix) => {
if m.args.len() != 0 { return Err(invalid_input()) }
Command::BOTSERV(suffix[])
},
None => {
if m.args.len() != 1 { return Err(invalid_input()) }
Command::BOTSERV(m.args[0][])
}
}
} else if let "HOSTSERV" = m.command[] {
match m.suffix {
Some(ref suffix) => {
if m.args.len() != 0 { return Err(invalid_input()) }
Command::HOSTSERV(suffix[])
},
None => {
if m.args.len() != 1 { return Err(invalid_input()) }
Command::HOSTSERV(m.args[0][])
}
}
} else if let "MEMOSERV" = m.command[] {
match m.suffix {
Some(ref suffix) => {
if m.args.len() != 0 { return Err(invalid_input()) }
Command::MEMOSERV(suffix[])
},
None => {
if m.args.len() != 1 { return Err(invalid_input()) }
Command::MEMOSERV(m.args[0][])
}
}
} else if let "CAP" = m.command[] {
if m.args.len() != 1 { return Err(invalid_input()) }
if let Some(cmd) = from_str(m.args[0][]) {

View file

@ -13,6 +13,8 @@ pub struct Config {
pub owners: Option<Vec<String>>,
/// The bot's nickname.
pub nickname: Option<String>,
/// The bot's NICKSERV password.
pub nick_password: Option<String>,
/// Alternative nicknames for the bots, if the default is taken.
pub alt_nicks: Option<Vec<String>>,
/// The bot's username.
@ -69,6 +71,13 @@ impl Config {
self.nickname.as_ref().map(|s| s[]).unwrap()
}
/// Gets the bot's nickserv password specified in the configuration.
/// This defaults to an empty string when not specified.
#[experimental]
pub fn nick_password(&self) -> &str {
self.nick_password.as_ref().map(|s| s[]).unwrap_or("")
}
/// Gets the alternate nicknames specified in the configuration.
/// This defaults to an empty vector when not specified.
#[experimental]
@ -157,6 +166,7 @@ mod test {
let cfg = Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
nick_password: None,
alt_nicks: None,
username: Some(format!("test")),
realname: Some(format!("test")),
@ -176,6 +186,7 @@ mod test {
let cfg = Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
nick_password: None,
alt_nicks: None,
username: Some(format!("test")),
realname: Some(format!("test")),

View file

@ -3,7 +3,7 @@
use std::io::IoResult;
use data::{Command, Config, User};
use data::Command::{CAP, INVITE, JOIN, KICK, KILL, MODE, NICK, NOTICE};
use data::Command::{CAP, INVITE, JOIN, KICK, KILL, MODE, NICK, NICKSERV, NOTICE};
use data::Command::{OPER, PASS, PONG, PRIVMSG, SAMODE, SANICK, TOPIC, USER};
use data::command::CapSubCommand::{END, REQ};
use data::kinds::{IrcReader, IrcWriter};
@ -51,8 +51,14 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
try!(self.server.send(PASS(self.server.config().password())));
}
try!(self.server.send(NICK(self.server.config().nickname())));
self.server.send(USER(self.server.config().username(), "0",
self.server.config().real_name()))
try!(self.server.send(USER(self.server.config().username(), "0",
self.server.config().real_name())));
if self.server.config().nick_password() != "" {
try!(self.server.send(NICKSERV(
format!("IDENTIFY {}", self.server.config().nick_password())[]
)));
}
Ok(())
}
/// Sends a PONG with the specified message.
@ -157,9 +163,11 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
#[cfg(test)]
mod test {
use super::Wrapper;
use std::default::Default;
use std::io::MemWriter;
use std::io::util::NullReader;
use conn::Connection;
use data::Config;
use server::IrcServer;
use server::test::{get_server_value, test_config};
@ -175,6 +183,44 @@ mod test {
"CAP REQ :multi-prefix\r\nCAP END\r\nNICK :test\r\nUSER test 0 * :test\r\n");
}
#[test]
fn identify_with_password() {
let server = IrcServer::from_connection(Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
alt_nicks: Some(vec![format!("test2")]),
server: Some(format!("irc.test.net")),
password: Some(format!("password")),
channels: Some(vec![format!("#test"), format!("#test2")]),
.. Default::default()
}, Connection::new(NullReader, MemWriter::new()));
{
let wrapper = Wrapper::new(&server);
wrapper.identify().unwrap();
}
assert_eq!(get_server_value(server)[], "CAP REQ :multi-prefix\r\nCAP END\r\n\
PASS :password\r\nNICK :test\r\nUSER test 0 * :test\r\n");
}
#[test]
fn identify_with_nick_password() {
let server = IrcServer::from_config(Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
alt_nicks: Some(vec![format!("test2")]),
server: Some(format!("irc.test.net")),
nick_password: Some(format!("password")),
channels: Some(vec![format!("#test"), format!("#test2")]),
.. Default::default()
}, Connection::new(NullReader, MemWriter::new()));
{
let wrapper = Wrapper::new(&server);
wrapper.identify().unwrap();
}
assert_eq!(get_server_value(server)[], "CAP REQ :multi-prefix\r\nCAP END\r\nNICK :test\r\n\
USER test 0 * :test\r\nNICKSERV :IDENTIFY password\r\n");
}
#[test]
fn send_pong() {
let server = IrcServer::from_connection(test_config(),