use std::error::Error;
use std::collections::HashSet;
use std::iter::FromIterator;
use std::time::{SystemTime, UNIX_EPOCH};
use chrono::prelude::*;
use super::*;
impl super::MainState {
async fn process_privmsg_notice<'a>(&self, conn_state: &mut ConnState,
targets: Vec<&'a str>, text: &'a str,
notice: bool) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
let user_nick = conn_state.user_state.nick.as_ref().unwrap();
let mut something_done = false;
{
let state = self.state.read().await;
for target in HashSet::<&&str>::from_iter(targets.iter()) {
let msg_str = if notice {
format!("NOTICE {} :{}", target, text)
} else { format!("PRIVMSG {} :{}", target, text) };
let (target_type, chan_str) = get_privmsg_target_type(target);
if target_type.contains(PrivMsgTargetType::Channel) { if let Some(chanobj) = state.channels.get(chan_str) {
let chanuser_mode = chanobj.users.get(user_nick);
let can_send = {
if (!chanobj.modes.no_external_messages &&
!chanobj.modes.secret) ||
chanuser_mode.is_some() {
true
} else {
if !notice {
self.feed_msg(&mut conn_state.stream, ErrCannotSendToChain404{
client, channel: chan_str }).await?;
}
false
}
};
let can_send = can_send && {
if !chanobj.modes.banned(&conn_state.user_state.source) {
true
} else {
if !notice {
self.feed_msg(&mut conn_state.stream, ErrCannotSendToChain404{
client, channel: chan_str }).await?;
}
false
}
};
let can_send = can_send && {
if !chanobj.modes.moderated ||
chanuser_mode.map_or(false, |chum| chum.is_voice()) {
true
} else {
if !notice {
self.feed_msg(&mut conn_state.stream, ErrCannotSendToChain404{
client, channel: chan_str }).await?;
}
false
}
};
if can_send {
use PrivMsgTargetType::*;
if !(target_type & ChannelAllSpecial).is_empty() {
if !(target_type & ChannelFounder).is_empty() {
if let Some(ref founders) = chanobj.modes.founders {
founders.iter().try_for_each(|u| {
if u != user_nick {
state.users.get(u).unwrap().send_msg_display(
&conn_state.user_state.source, &msg_str)
} else { Ok(()) }
})?;
}
}
if !(target_type & ChannelProtected).is_empty() {
if let Some(ref protecteds) = chanobj.modes.protecteds {
protecteds.iter().try_for_each(|u| {
if u != user_nick {
state.users.get(u).unwrap().send_msg_display(
&conn_state.user_state.source, &msg_str)
} else { Ok(()) }
})?;
}
}
if !(target_type & ChannelOper).is_empty() {
if let Some(ref operators) = chanobj.modes.operators {
operators.iter().try_for_each(|u| {
if u != user_nick {
state.users.get(u).unwrap().send_msg_display(
&conn_state.user_state.source, &msg_str)
} else { Ok(()) }
})?;
}
}
if !(target_type & ChannelHalfOper).is_empty() {
if let Some(ref half_ops) = chanobj.modes.half_operators {
half_ops.iter().try_for_each(|u| {
if u != user_nick {
state.users.get(u).unwrap().send_msg_display(
&conn_state.user_state.source, &msg_str)
} else { Ok(()) }
})?;
}
}
if !(target_type & ChannelVoice).is_empty() {
if let Some(ref voices) = chanobj.modes.voices {
voices.iter().try_for_each(|u| {
if u != user_nick {
state.users.get(u).unwrap().send_msg_display(
&conn_state.user_state.source, &msg_str)
} else { Ok(()) }
})?;
}
}
} else {
chanobj.users.keys().try_for_each(|u| {
if u != user_nick {
state.users.get(u).unwrap().send_msg_display(
&conn_state.user_state.source, &msg_str)
} else { Ok(()) }
})?;
}
something_done = true;
}
} else {
if !notice {
self.feed_msg(&mut conn_state.stream,
ErrNoSuchChannel403{ client, channel: chan_str }).await?;
}
}
} else { if let Some(cur_user) = state.users.get(*target) {
cur_user.send_msg_display(&conn_state.user_state.source, msg_str)?;
if !notice {
if let Some(ref away) = cur_user.away {
self.feed_msg(&mut conn_state.stream, RplAway301{ client,
nick: target, message: &away }).await?;
}
}
something_done = true;
} else {
if !notice {
self.feed_msg(&mut conn_state.stream, ErrNoSuchNick401{ client,
nick: target }).await?;
}
}
}
}
}
{ if something_done {
let mut state = self.state.write().await;
let mut user = state.users.get_mut(user_nick).unwrap();
user.last_activity = SystemTime::now().duration_since(UNIX_EPOCH)
.unwrap().as_secs();
}
}
Ok(())
}
pub(super) async fn process_privmsg<'a>(&self, conn_state: &mut ConnState,
targets: Vec<&'a str>, text: &'a str) -> Result<(), Box<dyn Error>> {
self.process_privmsg_notice(conn_state, targets, text, false).await
}
pub(super) async fn process_notice<'a>(&self, conn_state: &mut ConnState,
targets: Vec<&'a str>, text: &'a str) -> Result<(), Box<dyn Error>> {
self.process_privmsg_notice(conn_state, targets, text, true).await
}
pub(super) async fn send_who_info<'a>(&self, conn_state: &mut ConnState,
channel: Option<(&'a str, &ChannelUserModes)>, user_nick: &'a str,
user: &User, cmd_user: &User) -> Result<(), Box<dyn Error>> {
if !user.modes.invisible || !user.channels.is_disjoint(&cmd_user.channels) {
let client = conn_state.user_state.client_name();
let mut flags = String::new();
if user.away.is_some() { flags.push('G');
} else { flags.push('H'); }
if user.modes.is_local_oper() {
flags.push('*');
}
if let Some((_, ref chum)) = channel {
flags += &chum.to_string(&conn_state.caps);
}
self.feed_msg(&mut conn_state.stream, RplWhoReply352{ client,
channel: channel.map(|(c,_)| c).unwrap_or("*"), username: &user.name,
host: &user.hostname, server: &self.config.name, nick: &user_nick,
flags: &flags, hopcount: 0, realname: &user.realname}).await?;
}
Ok(())
}
pub(super) async fn process_who<'a>(&self, conn_state: &mut ConnState, mask: &'a str)
-> Result<(), Box<dyn Error>> {
let state = self.state.read().await;
let user_nick = conn_state.user_state.nick.as_ref().unwrap();
let user = state.users.get(user_nick).unwrap();
if mask.contains('*') || mask.contains('?') {
for (unick, u) in &state.users {
if match_wildcard(mask, &unick) || match_wildcard(mask, &u.source) ||
match_wildcard(mask, &u.realname) {
self.send_who_info(conn_state, None, &unick, &u, &user).await?;
}
}
} else if validate_channel(mask).is_ok() {
if let Some(channel) = state.channels.get(mask) {
for (u, chum) in &channel.users {
self.send_who_info(conn_state, Some((mask, chum)), &u,
state.users.get(u).unwrap(), &user).await?;
}
}
} else if validate_username(mask).is_ok() {
if let Some(ref arg_user) = state.users.get(mask) {
self.send_who_info(conn_state, None, mask, arg_user, &user).await?;
}
}
let client = conn_state.user_state.client_name();
self.feed_msg(&mut conn_state.stream, RplEndOfWho315{ client, mask }).await?;
Ok(())
}
pub(super) async fn process_whois<'a>(&self, conn_state: &mut ConnState,
target: Option<&'a str>, nickmasks: Vec<&'a str>) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
if target.is_some() {
self.feed_msg(&mut conn_state.stream, ErrUnknownError400{ client,
command: "WHOIS", subcommand: None, info: "Server unsupported" }).await?;
} else {
let state = self.state.read().await;
let user_nick = conn_state.user_state.nick.as_ref().unwrap();
let user = state.users.get(user_nick).unwrap();
let mut nicks = HashSet::<String>::new();
let mut real_nickmasks = vec![];
nickmasks.iter().for_each(|nickmask| {
if nickmask.contains('*') || nickmask.contains('?') {
real_nickmasks.push(nickmask);
} else {
if state.users.contains_key(&nickmask.to_string()) {
nicks.insert(nickmask.to_string());
}
}
});
if !real_nickmasks.is_empty() {
state.users.keys().for_each(|nick| {
if real_nickmasks.iter().any(|mask| match_wildcard(mask, nick)) {
nicks.insert(nick.to_string());
}
});
}
for nick in nicks {
let arg_user = state.users.get(&nick).unwrap();
if arg_user.modes.invisible &&
arg_user.channels.is_disjoint(&user.channels) { continue; }
if arg_user.modes.registered {
self.feed_msg(&mut conn_state.stream, RplWhoIsRegNick307{ client,
nick: &nick }).await?;
}
self.feed_msg(&mut conn_state.stream, RplWhoIsUser311{ client,
nick: &nick, username: &arg_user.name, host: &arg_user.hostname,
realname: &arg_user.realname }).await?;
self.feed_msg(&mut conn_state.stream, RplWhoIsServer312{ client,
nick: &nick, server: &self.config.name,
server_info: &self.config.info }).await?;
if arg_user.modes.is_local_oper() {
self.feed_msg(&mut conn_state.stream, RplWhoIsOperator313{ client,
nick: &nick }).await?;
}
let channel_replies = arg_user.channels.iter()
.filter_map(|chname| {
let ch = state.channels.get(chname).unwrap();
if !ch.modes.secret {
Some(WhoIsChannelStruct{ prefix: Some(ch.users.get(&nick)
.unwrap().to_string(&conn_state.caps)).clone(),
channel: chname })
} else { None }
}).collect::<Vec<_>>();
for chr_chunk in channel_replies.chunks(30) {
self.feed_msg(&mut conn_state.stream, RplWhoIsChannels319{ client,
nick: &nick, channels: &chr_chunk }).await?;
}
self.feed_msg(&mut conn_state.stream, RplwhoIsIdle317{ client,
nick: &nick, secs: SystemTime::now().duration_since(UNIX_EPOCH)
.unwrap().as_secs() - arg_user.last_activity,
signon: arg_user.signon }).await?;
if arg_user.modes.is_local_oper() {
self.feed_msg(&mut conn_state.stream, RplWhoIsHost378{ client,
nick: &nick, host_info: &arg_user.hostname }).await?;
self.feed_msg(&mut conn_state.stream, RplWhoIsModes379{ client,
nick: &nick, modes: &arg_user.modes.to_string() }).await?;
}
if conn_state.is_secure() {
self.feed_msg(&mut conn_state.stream, RplWhoIsSecure671{ client,
nick: &nick }).await?;
}
}
self.feed_msg(&mut conn_state.stream, RplEndOfWhoIs318{ client,
nick: &nickmasks.join(",") }).await?;
}
Ok(())
}
pub(super) async fn process_whowas<'a>(&self, conn_state: &mut ConnState,
nickname: &'a str, count: Option<usize>,
server: Option<&'a str>) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
if server.is_some() {
self.feed_msg(&mut conn_state.stream, ErrUnknownError400{ client,
command: "WHOWAS", subcommand: None, info: "Server unsupported" }).await?;
} else {
let state = self.state.read().await;
if let Some(hist) = state.nick_histories.get(&nickname.to_string()) {
let hist_count = if let Some(c) = count {
if c > 0 { c } else { hist.len() }
} else { hist.len() };
for entry in hist.iter().rev().take(hist_count) {
self.feed_msg(&mut conn_state.stream, RplWhoWasUser314{ client,
nick: &nickname, username: &entry.username,
host: &entry.hostname, realname: &entry.realname }).await?;
self.feed_msg(&mut conn_state.stream, RplWhoIsServer312{ client,
nick: &nickname, server: &self.config.name,
server_info: &format!("Logged in at {}",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(
entry.signon as i64, 0), Utc)) }).await?;
}
} else {
self.feed_msg(&mut conn_state.stream, ErrWasNoSuchNick406{ client,
nick: nickname }).await?;
}
self.feed_msg(&mut conn_state.stream, RplEndOfWhoWas369{ client,
nick: nickname }).await?;
}
Ok(())
}
pub(super) async fn process_kill<'a>(&self, conn_state: &mut ConnState, nickname: &'a str,
comment: &'a str) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
let mut state = self.state.write().await;
let user_nick = conn_state.user_state.nick.as_ref().unwrap();
let user = state.users.get(user_nick).unwrap();
if user.modes.oper {
if let Some(user_to_kill) = state.users.get_mut(nickname) {
if let Some(sender) = user_to_kill.quit_sender.take() {
sender.send((user_nick.to_string(), comment.to_string()))
.map_err(|_| "error".to_string())?;
}
} else {
self.feed_msg(&mut conn_state.stream, ErrNoSuchNick401{ client,
nick: nickname }).await?;
}
} else {
self.feed_msg(&mut conn_state.stream, ErrNoPrivileges481{ client }).await?;
}
Ok(())
}
pub(super) async fn process_rehash(&self, conn_state: &mut ConnState)
-> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
self.feed_msg(&mut conn_state.stream, ErrUnknownError400{ client,
command: "REHASH", subcommand: None,
info: "Server unsupported" }).await?;
Ok(())
}
pub(super) async fn process_restart(&self, conn_state: &mut ConnState)
-> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
self.feed_msg(&mut conn_state.stream, ErrUnknownError400{ client,
command: "RESTART", subcommand: None,
info: "Server unsupported" }).await?;
Ok(())
}
pub(super) async fn process_squit<'a>(&self, conn_state: &mut ConnState, server: &'a str,
comment: &'a str) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
if self.config.name != server {
self.feed_msg(&mut conn_state.stream, ErrUnknownError400{ client,
command: "SQUIT", subcommand: None, info: "Server unsupported" }).await?;
} else {
self.process_die(conn_state, Some(comment)).await?;
}
Ok(())
}
pub(super) async fn process_die<'a>(&self, conn_state: &mut ConnState,
message_opt: Option<&'a str>) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
let mut state = self.state.write().await;
let user_nick = conn_state.user_state.nick.as_ref().unwrap();
let user = state.users.get(user_nick).unwrap();
let message = message_opt.unwrap_or("Quitting from DIE");
if user.modes.oper {
for u in state.users.values_mut() {
if let Some(sender) = u.quit_sender.take() {
sender.send((user_nick.to_string(), message.to_string()))
.map_err(|_| "error".to_string())?;
}
}
if let Some(sender) = state.quit_sender.take() {
sender.send(message.to_string())?;
}
} else {
self.feed_msg(&mut conn_state.stream, ErrCantKillServer483{ client }).await?;
}
Ok(())
}
pub(super) async fn process_away<'a>(&self, conn_state: &mut ConnState,
text: Option<&'a str>) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
let mut state = self.state.write().await;
let user_nick = conn_state.user_state.nick.as_ref().unwrap();
let mut user = state.users.get_mut(user_nick).unwrap();
if let Some(t) = text {
user.away = Some(t.to_string());
self.feed_msg(&mut conn_state.stream, RplNowAway306{ client }).await?;
} else {
user.away = None;
self.feed_msg(&mut conn_state.stream, RplUnAway305{ client }).await?;
}
Ok(())
}
pub(super) async fn process_userhost<'a>(&self, conn_state: &mut ConnState,
nicknames: Vec<&'a str>) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
let state = self.state.read().await;
for nicks in nicknames.chunks(20) {
let replies = nicks.iter().filter_map(|nick|
if let Some(user) = state.users.get(&nick.to_string()) {
Some((nick, user))
} else { None }
).map(|(nick, user)| {
let asterisk = if user.modes.is_local_oper() { "*" } else { "" };
let away = if user.away.is_some() { '-' } else { '+' };
format!("{}{}={}~{}@{}", nick, asterisk, away, user.name, user.hostname) }
).collect::<Vec<_>>();
self.feed_msg(&mut conn_state.stream, RplUserHost302{ client,
replies: &replies }).await?;
}
Ok(())
}
pub(super) async fn process_wallops<'a>(&self, conn_state: &mut ConnState,
msg: &'a Message<'a>) -> Result<(), Box<dyn Error>> {
let state = self.state.read().await;
let user_nick = conn_state.user_state.nick.as_ref().unwrap();
let user = state.users.get(user_nick).unwrap();
if user.modes.is_local_oper() {
state.wallops_users.iter().try_for_each(|wu| state.users.get(wu).unwrap()
.send_message(msg, &conn_state.user_state.source))?;
} else {
let client = conn_state.user_state.client_name();
self.feed_msg(&mut conn_state.stream, ErrNoPrivileges481{ client }).await?;
}
Ok(())
}
pub(super) async fn process_ison<'a>(&self, conn_state: &mut ConnState,
nicknames: Vec<&'a str>) -> Result<(), Box<dyn Error>> {
let client = conn_state.user_state.client_name();
let state = self.state.read().await;
for nicks in nicknames.chunks(20) {
let outs = nicks.iter().filter(|nick|
state.users.contains_key(&nick.to_string()))
.map(|nick| *nick).collect::<Vec<_>>();
self.feed_msg(&mut conn_state.stream, RplIson303{ client,
nicknames: &outs }).await?;
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use super::super::test::*;
#[tokio::test]
async fn test_command_privmsg_user() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut line_stream2 = login_to_test_and_skip(port, "bowie", "bowie",
"Bowie Catcher").await;
line_stream.send("PRIVMSG bowie :Hello guy!".to_string()).await.unwrap();
assert_eq!(":alan!~alan@127.0.0.1 PRIVMSG bowie :Hello guy!".to_string(),
line_stream2.next().await.unwrap().unwrap());
line_stream2.send("PRIVMSG alan :Hello too!".to_string()).await.unwrap();
assert_eq!(":bowie!~bowie@127.0.0.1 PRIVMSG alan :Hello too!".to_string(),
line_stream.next().await.unwrap().unwrap());
line_stream.send("PRIVMSG boxie :Hello guy!".to_string()).await.unwrap();
assert_eq!(":irc.irc 401 alan boxie :No such nick/channel".to_string(),
line_stream.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
{
let mut state = main_state.state.write().await;
state.users.get_mut("bowie").unwrap().away = Some("Bye".to_string());
}
line_stream.send("PRIVMSG bowie :Hello guy too!".to_string()).await.unwrap();
assert_eq!(":irc.irc 301 alan bowie :Bye".to_string(),
line_stream.next().await.unwrap().unwrap());
assert_eq!(":alan!~alan@127.0.0.1 PRIVMSG bowie :Hello guy too!".to_string(),
line_stream2.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_privmsg_channel() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut line_stream2 = login_to_test_and_skip(port, "bowie", "bowie",
"Bowie Catcher").await;
let mut line_stream3 = login_to_test_and_skip(port, "cedric", "cedric",
"Cedric Maximus").await;
for line_stream in [&mut line_stream, &mut line_stream2, &mut line_stream3] {
line_stream.send("JOIN #channelx".to_string()).await.unwrap();
for _ in 0..3 { line_stream.next().await.unwrap().unwrap(); }
}
for _ in 0..2 { line_stream.next().await.unwrap().unwrap(); }
line_stream2.next().await.unwrap().unwrap();
line_stream.send("PRIVMSG #channelx :Hello guy!".to_string()).await.unwrap();
for line_stream in [&mut line_stream2, &mut line_stream3] {
assert_eq!(":alan!~alan@127.0.0.1 PRIVMSG #channelx :Hello guy!".to_string(),
line_stream.next().await.unwrap().unwrap());
}
line_stream3.send("PRIVMSG #channelx :Hi!".to_string()).await.unwrap();
for line_stream in [&mut line_stream, &mut line_stream2] {
assert_eq!(":cedric!~cedric@127.0.0.1 PRIVMSG #channelx :Hi!".to_string(),
line_stream.next().await.unwrap().unwrap());
}
line_stream.send("PRIVMSG #channely :Hello guy!".to_string()).await.unwrap();
assert_eq!(":irc.irc 403 alan #channely :No such channel".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_privmsg_channel_external_messages() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut line_stream2 = login_to_test_and_skip(port, "bowie", "bowie",
"Bowie Catcher").await;
line_stream.send("JOIN #channelx".to_string()).await.unwrap();
for _ in 0..3 { line_stream.next().await.unwrap().unwrap(); }
line_stream2.send("PRIVMSG #channelx :I want to join!".to_string())
.await.unwrap();
assert_eq!(":bowie!~bowie@127.0.0.1 PRIVMSG #channelx :I want to join!"
.to_string(), line_stream.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
{ main_state.state.write().await.channels.get_mut("#channelx").unwrap()
.modes.no_external_messages = true; }
line_stream2.send("PRIVMSG #channelx :I want to join!".to_string())
.await.unwrap();
assert_eq!(":irc.irc 404 bowie #channelx :Cannot send to channel".to_string(),
line_stream2.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_privmsg_channel_moderated() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut line_stream2 = login_to_test_and_skip(port, "bowie", "bowie",
"Bowie Catcher").await;
let mut line_stream3 = login_to_test_and_skip(port, "cedric", "cedric",
"Cedric Maximus").await;
line_stream.send("JOIN #channelx".to_string()).await.unwrap();
for _ in 0..3 { line_stream.next().await.unwrap().unwrap(); }
line_stream2.send("JOIN #channelx".to_string()).await.unwrap();
for _ in 0..3 { line_stream2.next().await.unwrap().unwrap(); }
line_stream.next().await.unwrap().unwrap();
time::sleep(Duration::from_millis(50)).await;
{ main_state.state.write().await.channels.get_mut("#channelx").unwrap()
.modes.moderated = true; }
line_stream.send("PRIVMSG #channelx :I want you!".to_string())
.await.unwrap();
assert_eq!(":alan!~alan@127.0.0.1 PRIVMSG #channelx :I want you!"
.to_string(), line_stream2.next().await.unwrap().unwrap());
line_stream2.send("PRIVMSG #channelx :I want you!".to_string())
.await.unwrap();
assert_eq!(":irc.irc 404 bowie #channelx :Cannot send to channel".to_string(),
line_stream2.next().await.unwrap().unwrap());
line_stream3.send("PRIVMSG #channelx :I want you too!".to_string())
.await.unwrap();
assert_eq!(":irc.irc 404 cedric #channelx :Cannot send to channel".to_string(),
line_stream3.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
{ main_state.state.write().await.channels.get_mut("#channelx").unwrap()
.add_voice("bowie"); }
line_stream2.send("PRIVMSG #channelx :I want you too!".to_string())
.await.unwrap();
assert_eq!(":bowie!~bowie@127.0.0.1 PRIVMSG #channelx :I want you too!"
.to_string(), line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_privmsg_channel_banned() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut line_stream2 = login_to_test_and_skip(port, "bowie", "bowie",
"Bowie Catcher").await;
line_stream.send("JOIN #channelx".to_string()).await.unwrap();
for _ in 0..3 { line_stream.next().await.unwrap().unwrap(); }
line_stream2.send("JOIN #channelx".to_string()).await.unwrap();
for _ in 0..3 { line_stream2.next().await.unwrap().unwrap(); }
line_stream.send("MODE #channelx +b bowie".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
line_stream.next().await.unwrap().unwrap();
line_stream2.next().await.unwrap().unwrap();
line_stream2.send("PRIVMSG #channelx :I want you!".to_string())
.await.unwrap();
assert_eq!(":irc.irc 404 bowie #channelx :Cannot send to channel".to_string(),
line_stream2.next().await.unwrap().unwrap());
line_stream.send("MODE #channelx +e bowie".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
line_stream.next().await.unwrap().unwrap();
line_stream.next().await.unwrap().unwrap();
line_stream2.next().await.unwrap().unwrap();
line_stream2.send("PRIVMSG #channelx :I want you!".to_string())
.await.unwrap();
assert_eq!(":bowie!~bowie@127.0.0.1 PRIVMSG #channelx :I want you!".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_privmsg_channel_prefixed() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut founder_stream1 = login_to_test_and_skip(port, "founder1", "founder1",
"Founder1").await;
let mut founder_stream2 = login_to_test_and_skip(port, "founder2", "founder2",
"Founder2").await;
let mut protected_stream1 = login_to_test_and_skip(port, "protected1",
"protected1", "Protected1").await;
let mut protected_stream2 = login_to_test_and_skip(port, "protected2",
"protected2", "Protected2").await;
let mut operator_stream1 = login_to_test_and_skip(port, "operator1",
"operator1", "Operator1").await;
let mut operator_stream2 = login_to_test_and_skip(port, "operator2",
"operator2", "Operator2").await;
let mut halfoper_stream1 = login_to_test_and_skip(port, "halfoper1",
"halfoper1", "HalfOper1").await;
let mut halfoper_stream2 = login_to_test_and_skip(port, "halfoper2",
"halfoper2", "HalfOper2").await;
let mut voice_stream1 = login_to_test_and_skip(port, "voice1",
"voice1", "Voice1").await;
let mut voice_stream2 = login_to_test_and_skip(port, "voice2",
"voice2", "Voice2").await;
for line_stream in [&mut line_stream, &mut founder_stream1, &mut founder_stream2,
&mut protected_stream1, &mut protected_stream2,
&mut operator_stream1, &mut operator_stream2,
&mut halfoper_stream1, &mut halfoper_stream2,
&mut voice_stream1, &mut voice_stream2] {
line_stream.send("JOIN #channely".to_string()).await.unwrap();
for _ in 0..3 { line_stream.next().await.unwrap().unwrap(); }
}
for (n,line_stream) in [&mut line_stream,
&mut founder_stream1, &mut founder_stream2,
&mut protected_stream1, &mut protected_stream2,
&mut operator_stream1, &mut operator_stream2,
&mut halfoper_stream1, &mut halfoper_stream2,
&mut voice_stream1, &mut voice_stream2].iter_mut().enumerate() {
for _ in 0..(10-n) { line_stream.next().await.unwrap().unwrap(); }
}
line_stream.send("MODE #channely +q founder1 +q founder2 \
+a protected1 +a protected2 +o operator1 +o operator2 \
+h halfoper1 +h halfoper2 +v voice1 +v voice2".to_string())
.await.unwrap();
for line_stream in [&mut line_stream,
&mut founder_stream1, &mut founder_stream2,
&mut protected_stream1, &mut protected_stream2,
&mut operator_stream1, &mut operator_stream2,
&mut halfoper_stream1, &mut halfoper_stream2,
&mut voice_stream1, &mut voice_stream2] {
line_stream.next().await.unwrap().unwrap();
}
for (ch, send_stream, send_nick, recv_stream, recv_nick) in [
('~', &mut founder_stream1, "founder1",
&mut founder_stream2, "founder2"),
('&', &mut protected_stream1, "protected1",
&mut protected_stream2, "protected2"),
('@', &mut operator_stream1, "operator1",
&mut operator_stream2, "operator2"),
('%', &mut halfoper_stream1, "halfoper1",
&mut halfoper_stream2, "halfoper2"),
('+', &mut voice_stream1, "voice1",
&mut voice_stream2, "voice2")] {
send_stream.send(format!("PRIVMSG {}#channely :Hello guys", ch))
.await.unwrap();
assert_eq!(format!(":{0}!~{0}@127.0.0.1 PRIVMSG {1}#channely :Hello guys",
send_nick, ch), recv_stream.next().await.unwrap().unwrap());
recv_stream.send(format!("PRIVMSG {}#channely :Hello guys", ch))
.await.unwrap();
assert_eq!(format!(":{0}!~{0}@127.0.0.1 PRIVMSG {1}#channely :Hello guys",
recv_nick, ch), send_stream.next().await.unwrap().unwrap());
}
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_privmsg_multiple() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut line_stream2 = login_to_test_and_skip(port, "bowie", "bowie",
"Bowie Catcher").await;
let mut line_stream3 = login_to_test_and_skip(port, "cedric", "cedric",
"Cedric Maximus").await;
line_stream.send("JOIN #channelx".to_string()).await.unwrap();
for _ in 0..3 { line_stream.next().await.unwrap().unwrap(); }
line_stream2.send("JOIN #channelx".to_string()).await.unwrap();
for _ in 0..3 { line_stream2.next().await.unwrap().unwrap(); }
line_stream.next().await.unwrap().unwrap();
line_stream.send("PRIVMSG #channelx,cedric :Hello boys".to_string())
.await.unwrap();
assert_eq!(":alan!~alan@127.0.0.1 PRIVMSG #channelx :Hello boys".to_string(),
line_stream2.next().await.unwrap().unwrap());
assert_eq!(":alan!~alan@127.0.0.1 PRIVMSG cedric :Hello boys".to_string(),
line_stream3.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_privmsg_activity() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut line_stream2 = login_to_test_and_skip(port, "bowie", "bowie",
"Bowie Catcher").await;
time::sleep(Duration::from_millis(50)).await;
let activity = {
let mut state = main_state.state.write().await;
state.users.get_mut("alan").unwrap().last_activity -= 10;
state.users.get("alan").unwrap().last_activity
};
line_stream.send("PRIVMSG guru :Hello boys".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
assert_eq!(":irc.irc 401 alan guru :No such nick/channel".to_string(),
line_stream.next().await.unwrap().unwrap());
{
let state = main_state.state.read().await;
assert_eq!(activity, state.users.get("alan").unwrap().last_activity);
}
line_stream.send("PRIVMSG bowie :Hello boys".to_string()).await.unwrap();
assert_eq!(":alan!~alan@127.0.0.1 PRIVMSG bowie :Hello boys".to_string(),
line_stream2.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
{
let state = main_state.state.read().await;
assert_ne!(activity, state.users.get("alan").unwrap().last_activity);
}
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_notice() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "alan", "alan",
"Alan Bodarski").await;
let mut line_stream2 = login_to_test_and_skip(port, "bowie", "bowie",
"Bowie Catcher").await;
let mut cedric_stream = login_to_test_and_skip(port, "cedric", "cedric",
"Cedric Maximus").await;
time::sleep(Duration::from_millis(50)).await;
{
let mut state = main_state.state.write().await;
state.users.get_mut("cedric").unwrap().away = Some("Bye".to_string());
}
line_stream.send("NOTICE #chan1,guru,cedric :Hello boys".to_string())
.await.unwrap();
line_stream2.send("PRIVMSG alan :Hello boys".to_string()).await.unwrap();
assert_eq!(":bowie!~bowie@127.0.0.1 PRIVMSG alan :Hello boys".to_string(),
line_stream.next().await.unwrap().unwrap());
cedric_stream.send("QUIT :Bye".to_string()).await.unwrap();
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_who() {
let mut config = MainConfig::default();
config.operators = Some(vec![
OperatorConfig{ name: "fanny".to_string(),
password: argon2_hash_password("Funny"), mask: None },
]);
let (main_state, handle, port) = run_test_server(config).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
let mut line_stream2 = login_to_test_and_skip(port, "jerry", "jerry",
"Jerry Lazy").await;
line_stream.send("WHO jerry".to_string()).await.unwrap();
assert_eq!(":irc.irc 352 fanny * ~jerry 127.0.0.1 irc.irc jerry H :0 \
Jerry Lazy".to_string(), line_stream.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 315 fanny jerry :End of WHO list".to_string(),
line_stream.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
{
let mut state = main_state.state.write().await;
state.users.get_mut("jerry").unwrap().away = Some("Bye".to_string());
}
line_stream.send("WHO jerry".to_string()).await.unwrap();
assert_eq!(":irc.irc 352 fanny * ~jerry 127.0.0.1 irc.irc jerry G :0 \
Jerry Lazy".to_string(), line_stream.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 315 fanny jerry :End of WHO list".to_string(),
line_stream.next().await.unwrap().unwrap());
line_stream2.send("WHO fanny".to_string()).await.unwrap();
assert_eq!(":irc.irc 352 jerry * ~fanny 127.0.0.1 irc.irc fanny H* :0 \
Fanny BumBumBum".to_string(),
line_stream2.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 315 jerry fanny :End of WHO list".to_string(),
line_stream2.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
fn equal_list<'a>(msg_start: &'a str, expected :&'a[&'a str],
results :&'a[&'a str]) -> bool {
let mut expected_sorted = Vec::from(expected);
expected_sorted.sort();
let mut touched = vec![false; expected.len()];
results.iter().all(|res| {
if res.starts_with(msg_start) {
let rest = &res[msg_start.len()..];
if let Ok(p) = expected_sorted.binary_search(&rest) {
touched[p] = true;
true
} else { false }
} else { false }
}) && touched.iter().all(|x| *x)
}
#[tokio::test]
async fn test_command_who_channel() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut line_stream2 = login_to_test_and_skip(port, "jerry", "jerry",
"Jerry Lazy").await;
for line_stream in [&mut line_stream, &mut line_stream2] {
line_stream.send("JOIN #channelz".to_string()).await.unwrap();
for _ in 0..3 { line_stream.next().await.unwrap().unwrap(); }
}
line_stream.next().await.unwrap().unwrap();
line_stream.send("WHO #channelz".to_string()).await.unwrap();
assert!(equal_list(":irc.irc 352 fanny #channelz ",
&["~fanny 127.0.0.1 irc.irc fanny H~ :0 Fanny BumBumBum",
"~jerry 127.0.0.1 irc.irc jerry H :0 Jerry Lazy"],
&[&line_stream.next().await.unwrap().unwrap(),
&line_stream.next().await.unwrap().unwrap()]));
assert_eq!(":irc.irc 315 fanny #channelz :End of WHO list".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_who_channel_multi_prefix() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = connect_to_test(port).await;
line_stream.send("CAP LS 302".to_string()).await.unwrap();
line_stream.send("NICK fanny".to_string()).await.unwrap();
line_stream.send("USER fanny 8 * :Fanny BumBumBum".to_string()).await.unwrap();
line_stream.send("CAP REQ :multi-prefix".to_string()).await.unwrap();
line_stream.send("CAP END".to_string()).await.unwrap();
for _ in 0..20 { line_stream.next().await.unwrap().unwrap(); }
let mut line_stream2 = login_to_test_and_skip(port, "jerry", "jerry",
"Jerry Lazy").await;
for line_stream in [&mut line_stream, &mut line_stream2] {
line_stream.send("JOIN #channelz".to_string()).await.unwrap();
for _ in 0..3 { line_stream.next().await.unwrap().unwrap(); }
}
line_stream.next().await.unwrap().unwrap();
line_stream.send("WHO #channelz".to_string()).await.unwrap();
assert!(equal_list(":irc.irc 352 fanny #channelz ",
&["~fanny 127.0.0.1 irc.irc fanny H~@ :0 Fanny BumBumBum",
"~jerry 127.0.0.1 irc.irc jerry H :0 Jerry Lazy"],
&[&line_stream.next().await.unwrap().unwrap(),
&line_stream.next().await.unwrap().unwrap()]));
assert_eq!(":irc.irc 315 fanny #channelz :End of WHO list".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_who_wildcards() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut stream1 = login_to_test_and_skip(port, "jerry", "jerry",
"Jerry Lazy").await;
let mut stream2 = login_to_test_and_skip(port, "jarry", "jarry",
"Jarry Lazy").await;
let mut stream3 = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
line_stream.send("WHO j*rry".to_string()).await.unwrap();
assert!(equal_list(":irc.irc 352 fanny * ",
&["~jerry 127.0.0.1 irc.irc jerry H :0 Jerry Lazy",
"~jarry 127.0.0.1 irc.irc jarry H :0 Jarry Lazy"],
&[&line_stream.next().await.unwrap().unwrap(),
&line_stream.next().await.unwrap().unwrap()]));
assert_eq!(":irc.irc 315 fanny j*rry :End of WHO list".to_string(),
line_stream.next().await.unwrap().unwrap());
line_stream.send("WHO *rry".to_string()).await.unwrap();
assert!(equal_list(":irc.irc 352 fanny * ",
&["~jerry 127.0.0.1 irc.irc jerry H :0 Jerry Lazy",
"~jarry 127.0.0.1 irc.irc jarry H :0 Jarry Lazy",
"~harry 127.0.0.1 irc.irc harry H :0 Harry Lazy"],
&[&line_stream.next().await.unwrap().unwrap(),
&line_stream.next().await.unwrap().unwrap(),
&line_stream.next().await.unwrap().unwrap()]));
assert_eq!(":irc.irc 315 fanny *rry :End of WHO list".to_string(),
line_stream.next().await.unwrap().unwrap());
for stream in [&mut stream1, &mut stream2, &mut stream3] {
stream.send("QUIT :Bye".to_string()).await.unwrap();
}
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_who_invisible() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut jerry_stream = login_to_test_and_skip(port, "jerry", "jerry",
"Jerry Lazy").await;
let mut jarry_stream = login_to_test_and_skip(port, "jarry", "jarry",
"Jarry Lazy").await;
let mut harry_stream = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
time::sleep(Duration::from_millis(50)).await;
jarry_stream.send("MODE jarry +i".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
line_stream.send("WHO *rry".to_string()).await.unwrap();
assert!(equal_list(":irc.irc 352 fanny * ",
&["~jerry 127.0.0.1 irc.irc jerry H :0 Jerry Lazy",
"~harry 127.0.0.1 irc.irc harry H :0 Harry Lazy"],
&[&line_stream.next().await.unwrap().unwrap(),
&line_stream.next().await.unwrap().unwrap()]));
assert_eq!(":irc.irc 315 fanny *rry :End of WHO list".to_string(),
line_stream.next().await.unwrap().unwrap());
for stream in [&mut jerry_stream, &mut jarry_stream, &mut harry_stream] {
stream.send("QUIT :Bye".to_string()).await.unwrap();
}
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_who_invisible_channel() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut jerry_stream = login_to_test_and_skip(port, "jerry", "jerry",
"Jerry Lazy").await;
let mut jarry_stream = login_to_test_and_skip(port, "jarry", "jarry",
"Jarry Lazy").await;
let mut harry_stream = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
line_stream.send("JOIN #mychannel".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
jarry_stream.send("JOIN #superchannel".to_string()).await.unwrap();
jarry_stream.send("MODE jarry +i".to_string()).await.unwrap();
harry_stream.send("JOIN #mychannel".to_string()).await.unwrap();
harry_stream.send("MODE harry +i".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
for _ in 0..(3+1) { line_stream.next().await.unwrap().unwrap(); }
line_stream.send("WHO *rry".to_string()).await.unwrap();
assert!(equal_list(":irc.irc 352 fanny * ",
&["~jerry 127.0.0.1 irc.irc jerry H :0 Jerry Lazy",
"~harry 127.0.0.1 irc.irc harry H :0 Harry Lazy"],
&[&line_stream.next().await.unwrap().unwrap(),
&line_stream.next().await.unwrap().unwrap()]));
assert_eq!(":irc.irc 315 fanny *rry :End of WHO list".to_string(),
line_stream.next().await.unwrap().unwrap());
jerry_stream.send("QUIT :Bye".to_string()).await.unwrap();
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_whois() {
let mut config = MainConfig::default();
config.operators = Some(vec![
OperatorConfig{ name: "fanny".to_string(),
password: argon2_hash_password("Funny"), mask: None },
]);
let (main_state, handle, port) = run_test_server(config).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
let mut harry_stream = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
time::sleep(Duration::from_millis(50)).await;
let signon = { main_state.state.read().await.users
.get("harry").unwrap().signon };
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
line_stream.send("WHOIS harry".to_string()).await.unwrap();
for expected in [
":irc.irc 311 fanny harry ~harry 127.0.0.1 * :Harry Lazy",
":irc.irc 312 fanny harry irc.irc :This is IRC server",
&format!(":irc.irc 317 fanny harry {} {} :seconds idle, signon time",
now - signon, signon),
":irc.irc 318 fanny harry :End of /WHOIS list" ] {
assert_eq!(expected, line_stream.next().await.unwrap().unwrap());
}
let signon = { main_state.state.read().await.users
.get("fanny").unwrap().signon };
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
harry_stream.send("WHOIS fanny".to_string()).await.unwrap();
for expected in [
":irc.irc 311 harry fanny ~fanny 127.0.0.1 * :Fanny BumBumBum",
":irc.irc 312 harry fanny irc.irc :This is IRC server",
":irc.irc 313 harry fanny :is an IRC operator",
&format!(":irc.irc 317 harry fanny {} {} :seconds idle, signon time",
now - signon, signon),
":irc.irc 378 harry fanny :is connecting from 127.0.0.1",
":irc.irc 379 harry fanny :is using modes +o",
":irc.irc 318 harry fanny :End of /WHOIS list" ] {
assert_eq!(expected, harry_stream.next().await.unwrap().unwrap());
}
}
quit_test_server(main_state, handle).await;
}
#[cfg(any(feature = "tls_rustls", feature = "tls_openssl"))]
#[tokio::test]
async fn test_command_whois_tls() {
let (main_state, handle, port) = run_test_tls_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_tls_and_skip(port,
"fanny", "fanny", "Fanny BumBumBum").await;
let mut harry_stream = login_to_test_tls_and_skip(port,
"harry", "harry", "Harry Lazy").await;
time::sleep(Duration::from_millis(50)).await;
let signon = { main_state.state.read().await.users
.get("harry").unwrap().signon };
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
line_stream.send("WHOIS harry".to_string()).await.unwrap();
for expected in [
":irc.irc 311 fanny harry ~harry 127.0.0.1 * :Harry Lazy",
":irc.irc 312 fanny harry irc.irc :This is IRC server",
&format!(":irc.irc 317 fanny harry {} {} :seconds idle, signon time",
now - signon, signon),
":irc.irc 671 fanny harry :is using a secure connection",
":irc.irc 318 fanny harry :End of /WHOIS list" ] {
assert_eq!(expected, line_stream.next().await.unwrap().unwrap());
}
harry_stream.send("QUIT :Bye".to_string()).await.unwrap();
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_whois_wilcards() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut stream1 = login_to_test_and_skip(port, "henry", "henry",
"Henry Solo").await;
let mut stream2 = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
let mut stream3 = login_to_test_and_skip(port, "dizzy", "dizzy",
"Dizzy Multi").await;
time::sleep(Duration::from_millis(50)).await;
let (signon, signon2) = {
let state = main_state.state.read().await;
(state.users.get("harry").unwrap().signon,
state.users.get("henry").unwrap().signon)
};
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
line_stream.send("WHOIS *ry".to_string()).await.unwrap();
let mut expecteds = [ ":irc.irc 311 fanny harry ~harry 127.0.0.1 * :Harry Lazy",
":irc.irc 312 fanny harry irc.irc :This is IRC server",
&format!(":irc.irc 317 fanny harry {} {} :seconds idle, signon time",
now - signon, signon),
":irc.irc 311 fanny henry ~henry 127.0.0.1 * :Henry Solo",
":irc.irc 312 fanny henry irc.irc :This is IRC server",
&format!(":irc.irc 317 fanny henry {} {} :seconds idle, signon time",
now - signon2, signon2),
].iter().map(|x| x.to_string()).collect::<Vec<_>>();
expecteds.sort();
let mut results = vec![];
for _ in 0..6 { results.push(line_stream.next().await.unwrap().unwrap()); }
results.sort();
assert_eq!(expecteds, results);
assert_eq!(":irc.irc 318 fanny *ry :End of /WHOIS list".to_string(),
line_stream.next().await.unwrap().unwrap());
for stream in [&mut stream1, &mut stream2, &mut stream3] {
stream.send("QUIT :Bye".to_string()).await.unwrap();
}
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_whois_invisible_channel() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut jerry_stream = login_to_test_and_skip(port, "jerry", "jerry",
"Jerry Lazy").await;
let mut jarry_stream = login_to_test_and_skip(port, "jarry", "jarry",
"Jarry Lazy").await;
let mut harry_stream = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
line_stream.send("JOIN #mychannel".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
jarry_stream.send("JOIN #superchannel".to_string()).await.unwrap();
jarry_stream.send("MODE jarry +i".to_string()).await.unwrap();
harry_stream.send("JOIN #mychannel".to_string()).await.unwrap();
harry_stream.send("MODE harry +i".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
for _ in 0..(3+1) { line_stream.next().await.unwrap().unwrap(); }
time::sleep(Duration::from_millis(50)).await;
let (signon, signon2) = {
let state = main_state.state.read().await;
(state.users.get("harry").unwrap().signon,
state.users.get("jerry").unwrap().signon)
};
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
line_stream.send("WHOIS *rry".to_string()).await.unwrap();
let mut expecteds = [
":irc.irc 311 fanny harry ~harry 127.0.0.1 * :Harry Lazy",
":irc.irc 312 fanny harry irc.irc :This is IRC server",
":irc.irc 319 fanny harry :#mychannel",
&format!(":irc.irc 317 fanny harry {} {} :seconds idle, signon time",
now - signon, signon),
":irc.irc 311 fanny jerry ~jerry 127.0.0.1 * :Jerry Lazy",
":irc.irc 312 fanny jerry irc.irc :This is IRC server",
&format!(":irc.irc 317 fanny jerry {} {} :seconds idle, signon time",
now - signon2, signon2)
].iter().map(|x| x.to_string()).collect::<Vec<_>>();
expecteds.sort();
let mut results = vec![];
for _ in 0..7 { results.push(line_stream.next().await.unwrap().unwrap()); }
results.sort();
assert_eq!(expecteds, results);
assert_eq!(":irc.irc 318 fanny *rry :End of /WHOIS list".to_string(),
line_stream.next().await.unwrap().unwrap());
jerry_stream.send("QUIT :Bye".to_string()).await.unwrap();
}
quit_test_server(main_state, handle).await;
}
fn equal_channel_names<'a>(exp_msg: &'a str, exp_names: &'a[&'a str],
names_replies: &'a[&'a str]) -> bool {
let mut exp_names_sorted = Vec::from(exp_names);
exp_names_sorted.sort();
let mut touched = vec![false; exp_names.len()];
names_replies.iter().all(|reply| {
if reply.starts_with(exp_msg) {
reply[exp_msg.len()..].split_terminator(" ").all(|c| {
if let Ok(p) = exp_names_sorted.binary_search(&c) {
touched[p] = true;
true
} else { false }
})
} else { false }
}) && touched.iter().all(|x| *x)
}
#[tokio::test]
async fn test_command_whois_channels() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut harry_stream = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
for i in 0..45 {
harry_stream.send(format!("JOIN #channel{}", i)).await.unwrap();
for _ in 0..3 { harry_stream.next().await.unwrap().unwrap(); }
}
time::sleep(Duration::from_millis(50)).await;
let signon = { main_state.state.read().await.users
.get("harry").unwrap().signon };
line_stream.send("WHOIS harry".to_string()).await.unwrap();
assert_eq!(":irc.irc 311 fanny harry ~harry 127.0.0.1 * :Harry Lazy"
.to_string(), line_stream.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 312 fanny harry irc.irc :This is IRC server"
.to_string(), line_stream.next().await.unwrap().unwrap());
let channel_list = (0..45).map(|x| format!("~#channel{}", x))
.collect::<Vec<_>>();
let channel_str_list = channel_list.iter().map(|x| x.as_str())
.collect::<Vec<_>>();
assert!(equal_channel_names(":irc.irc 319 fanny harry :",
channel_str_list.as_slice(),
&[&line_stream.next().await.unwrap().unwrap(),
&line_stream.next().await.unwrap().unwrap()]));
assert_eq!(format!(
":irc.irc 317 fanny harry {} {} :seconds idle, signon time",
SystemTime::now().duration_since(UNIX_EPOCH)
.unwrap().as_secs() - signon, signon),
line_stream.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 318 fanny harry :End of /WHOIS list".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_whois_channel_multi_prefix() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = connect_to_test(port).await;
line_stream.send("CAP LS 302".to_string()).await.unwrap();
line_stream.send("NICK fanny".to_string()).await.unwrap();
line_stream.send("USER fanny 8 * :Fanny BumBumBum".to_string()).await.unwrap();
line_stream.send("CAP REQ :multi-prefix".to_string()).await.unwrap();
line_stream.send("CAP END".to_string()).await.unwrap();
for _ in 0..20 { line_stream.next().await.unwrap().unwrap(); }
let mut harry_stream = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
harry_stream.send("JOIN #channel2".to_string()).await.unwrap();
for _ in 0..3 { harry_stream.next().await.unwrap().unwrap(); }
time::sleep(Duration::from_millis(50)).await;
let signon = { main_state.state.read().await.users
.get("harry").unwrap().signon };
line_stream.send("WHOIS harry".to_string()).await.unwrap();
assert_eq!(":irc.irc 311 fanny harry ~harry 127.0.0.1 * :Harry Lazy"
.to_string(), line_stream.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 312 fanny harry irc.irc :This is IRC server"
.to_string(), line_stream.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 319 fanny harry :~@#channel2"
.to_string(), line_stream.next().await.unwrap().unwrap());
assert_eq!(format!(
":irc.irc 317 fanny harry {} {} :seconds idle, signon time",
SystemTime::now().duration_since(UNIX_EPOCH)
.unwrap().as_secs() - signon, signon),
line_stream.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 318 fanny harry :End of /WHOIS list".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_whois_multiple() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut henry_stream = login_to_test_and_skip(port, "henry", "henry",
"Henry Solo").await;
let mut harry_stream = login_to_test_and_skip(port, "harry", "harry",
"Harry Lazy").await;
let mut dizzy_stream = login_to_test_and_skip(port, "dizzy", "dizzy",
"Dizzy Multi").await;
time::sleep(Duration::from_millis(50)).await;
let (signon, signon2, signon3) = {
let state = main_state.state.read().await;
(state.users.get("harry").unwrap().signon,
state.users.get("henry").unwrap().signon,
state.users.get("dizzy").unwrap().signon)
};
line_stream.send("WHOIS *ry,dizzy".to_string()).await.unwrap();
let mut expecteds = [ ":irc.irc 311 fanny harry ~harry 127.0.0.1 * :Harry Lazy",
":irc.irc 312 fanny harry irc.irc :This is IRC server",
&format!(":irc.irc 317 fanny harry {} {} :seconds idle, signon time",
SystemTime::now().duration_since(UNIX_EPOCH)
.unwrap().as_secs() - signon, signon),
":irc.irc 311 fanny henry ~henry 127.0.0.1 * :Henry Solo",
":irc.irc 312 fanny henry irc.irc :This is IRC server",
&format!(":irc.irc 317 fanny henry {} {} :seconds idle, signon time",
SystemTime::now().duration_since(UNIX_EPOCH)
.unwrap().as_secs() - signon2, signon2),
":irc.irc 311 fanny dizzy ~dizzy 127.0.0.1 * :Dizzy Multi",
":irc.irc 312 fanny dizzy irc.irc :This is IRC server",
&format!(":irc.irc 317 fanny dizzy {} {} :seconds idle, signon time",
SystemTime::now().duration_since(UNIX_EPOCH)
.unwrap().as_secs() - signon3, signon3),
].iter().map(|x| x.to_string()).collect::<Vec<_>>();
expecteds.sort();
let mut results = vec![];
for _ in 0..9 { results.push(line_stream.next().await.unwrap().unwrap()); }
results.sort();
assert_eq!(expecteds, results);
assert_eq!(":irc.irc 318 fanny *ry,dizzy :End of /WHOIS list".to_string(),
line_stream.next().await.unwrap().unwrap());
for stream in [&mut henry_stream, &mut harry_stream, &mut dizzy_stream] {
stream.send("QUIT :Bye".to_string()).await.unwrap();
}
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_whowas() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
let mut dizzy_stream = login_to_test_and_skip(port, "dizzy", "dizzy",
"Dizzy Multi").await;
dizzy_stream.send("QUIT :Bye".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
let mut dizzy_stream = login_to_test_and_skip(port, "dizzy", "dizzy",
"Dizzy MultiX").await;
dizzy_stream.send("QUIT :Bye".to_string()).await.unwrap();
time::sleep(Duration::from_millis(50)).await;
let (signon1, signon2) = {
let state = main_state.state.read().await;
let history = state.nick_histories.get("dizzy").unwrap();
(history[1].signon, history[0].signon)
};
line_stream.send("WHOWAS dizzy".to_string()).await.unwrap();
for expected in [
":irc.irc 314 fanny dizzy ~dizzy 127.0.0.1 * :Dizzy MultiX",
&format!(":irc.irc 312 fanny dizzy irc.irc :Logged in at {}",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(
signon1 as i64, 0), Utc)),
":irc.irc 314 fanny dizzy ~dizzy 127.0.0.1 * :Dizzy Multi",
&format!(":irc.irc 312 fanny dizzy irc.irc :Logged in at {}",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(
signon2 as i64, 0), Utc)) ] {
assert_eq!(expected.to_string(), line_stream.next().await.unwrap().unwrap());
}
assert_eq!(":irc.irc 369 fanny dizzy :End of WHOWAS".to_string(),
line_stream.next().await.unwrap().unwrap());
line_stream.send("WHOWAS dizzy 1".to_string()).await.unwrap();
for expected in [
":irc.irc 314 fanny dizzy ~dizzy 127.0.0.1 * :Dizzy MultiX",
&format!(":irc.irc 312 fanny dizzy irc.irc :Logged in at {}",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(
signon1 as i64, 0), Utc)) ] {
assert_eq!(expected.to_string(), line_stream.next().await.unwrap().unwrap());
}
assert_eq!(":irc.irc 369 fanny dizzy :End of WHOWAS".to_string(),
line_stream.next().await.unwrap().unwrap());
let mut dizzy_stream = login_to_test_and_skip(port, "dizzy", "dizzy",
"Dizzy MultiZ").await;
dizzy_stream.send("NICK gizzy".to_string()).await.unwrap();
dizzy_stream.next().await.unwrap().unwrap();
line_stream.next().await.unwrap().unwrap();
time::sleep(Duration::from_millis(50)).await;
let signon0 = {
let state = main_state.state.read().await;
let history = state.nick_histories.get("dizzy").unwrap();
history[2].signon
};
line_stream.send("WHOWAS dizzy".to_string()).await.unwrap();
for expected in [
":irc.irc 314 fanny dizzy ~dizzy 127.0.0.1 * :Dizzy MultiZ",
&format!(":irc.irc 312 fanny dizzy irc.irc :Logged in at {}",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(
signon0 as i64, 0), Utc)),
":irc.irc 314 fanny dizzy ~dizzy 127.0.0.1 * :Dizzy MultiX",
&format!(":irc.irc 312 fanny dizzy irc.irc :Logged in at {}",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(
signon1 as i64, 0), Utc)),
":irc.irc 314 fanny dizzy ~dizzy 127.0.0.1 * :Dizzy Multi",
&format!(":irc.irc 312 fanny dizzy irc.irc :Logged in at {}",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(
signon2 as i64, 0), Utc)) ] {
assert_eq!(expected.to_string(), line_stream.next().await.unwrap().unwrap());
}
assert_eq!(":irc.irc 369 fanny dizzy :End of WHOWAS".to_string(),
line_stream.next().await.unwrap().unwrap());
line_stream.send("WHOWAS zizzy".to_string()).await.unwrap();
assert_eq!(":irc.irc 406 fanny zizzy :There was no such nickname".to_string(),
line_stream.next().await.unwrap().unwrap());
assert_eq!(":irc.irc 369 fanny zizzy :End of WHOWAS".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_kill() {
let mut config = MainConfig::default();
config.operators = Some(vec![
OperatorConfig{ name: "fanny".to_string(),
password: argon2_hash_password("Funny"), mask: None },
]);
let (main_state, handle, port) = run_test_server(config).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
let mut dizzy_stream = login_to_test_and_skip(port, "dizzy", "dizzy",
"Dizzy Multi").await;
line_stream.send("KILL dizzy :Not polite".to_string()).await.unwrap();
assert_eq!(":irc.irc ERROR :User killed by fanny: Not polite".to_string(),
dizzy_stream.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
assert!(!main_state.state.read().await.users.contains_key("dizzy"));
line_stream.send("KILL dizzy :Not polite".to_string()).await.unwrap();
assert_eq!(":irc.irc 401 fanny dizzy :No such nick/channel".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_kill_no_privileges() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
login_to_test_and_skip(port, "dizzy", "dizzy", "Dizzy Multi").await;
line_stream.send("KILL dizzy :Not polite".to_string()).await.unwrap();
assert_eq!(":irc.irc 481 fanny :Permission Denied- You're not an IRC \
operator".to_string(), line_stream.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
assert!(!main_state.state.read().await.users.contains_key("dizzy"));
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_squit() {
let mut config = MainConfig::default();
config.operators = Some(vec![
OperatorConfig{ name: "fanny".to_string(),
password: argon2_hash_password("Funny"), mask: None },
]);
let (_, _, port) = run_test_server(config).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
line_stream.send("SQUIT irc.irc :Blabla".to_string()).await.unwrap();
}
}
#[tokio::test]
async fn test_command_die() {
let mut config = MainConfig::default();
config.operators = Some(vec![
OperatorConfig{ name: "fanny".to_string(),
password: argon2_hash_password("Funny"), mask: None },
]);
let (_, _, port) = run_test_server(config).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
line_stream.send("DIE :Blabla".to_string()).await.unwrap();
}
}
#[tokio::test]
async fn test_command_die_no_message() {
let mut config = MainConfig::default();
config.operators = Some(vec![
OperatorConfig{ name: "fanny".to_string(),
password: argon2_hash_password("Funny"), mask: None },
]);
let (_, _, port) = run_test_server(config).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
line_stream.send("DIE".to_string()).await.unwrap();
}
}
#[tokio::test]
async fn test_command_squit_no_privileges() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("SQUIT irc.irc :Blabla".to_string()).await.unwrap();
assert_eq!(":irc.irc 483 fanny :You cant kill a server!".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_away() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "fanny", "fanny",
"Fanny BumBumBum").await;
line_stream.send("AWAY :See later".to_string()).await.unwrap();
assert_eq!(":irc.irc 306 fanny :You have been marked as being away".to_string(),
line_stream.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
assert_eq!(Some("See later".to_string()),
main_state.state.read().await.users.get("fanny").unwrap().away);
line_stream.send("AWAY".to_string()).await.unwrap();
assert_eq!(":irc.irc 305 fanny :You are no longer marked as being away"
.to_string(), line_stream.next().await.unwrap().unwrap());
time::sleep(Duration::from_millis(50)).await;
assert!(main_state.state.read().await.users.get("fanny")
.unwrap().away.is_none());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_userhost() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "funny", "funny",
"Bunny BumBumBum").await;
let mut xstreams = vec![];
for i in 0..50 {
xstreams.push(login_to_test_and_skip(port, &format!("binny{}", i),
&format!("binny{}", i), &format!("Binny{} BigBang", i)).await);
}
line_stream.send(format!("USERHOST {}", (0..50).map(|x| format!("binny{}", x))
.collect::<Vec<_>>().join(" "))).await.unwrap();
for range in [(0..20), (20..40), (40..50)] {
assert_eq!(":irc.irc 302 funny :".to_string() + &(range.map(
|x| format!("binny{0}=+~binny{0}@127.0.0.1", x))
.collect::<Vec<_>>().join(" ")),
line_stream.next().await.unwrap().unwrap());
}
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_userhost_away() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "funny", "funny",
"Bunny BumBumBum").await;
line_stream.send("AWAY :blablam".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
line_stream.send("USERHOST funny".to_string()).await.unwrap();
assert_eq!(":irc.irc 302 funny :funny=-~funny@127.0.0.1".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_userhost_oper() {
let mut config = MainConfig::default();
config.operators = Some(vec![
OperatorConfig{ name: "fanny".to_string(),
password: argon2_hash_password("Funny"), mask: None },
]);
let (main_state, handle, port) = run_test_server(config).await;
{
let mut line_stream = login_to_test_and_skip(port, "funny", "funny",
"Funny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
line_stream.send("USERHOST funny".to_string()).await.unwrap();
assert_eq!(":irc.irc 302 funny :funny*=+~funny@127.0.0.1".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_userhost_notfound() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "funny", "funny",
"Bunny BumBumBum").await;
line_stream.send("USERHOST ziggy".to_string()).await.unwrap();
assert_eq!(":irc.irc 302 funny :".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_ison() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "funny", "funny",
"Bunny BumBumBum").await;
let mut xstreams = vec![];
for i in 0..50 {
xstreams.push(login_to_test_and_skip(port, &format!("binny{}", i),
&format!("binny{}", i), &format!("Binny{} BigBang", i)).await);
}
line_stream.send(format!("ISON {}", (0..50).map(|x| format!("binny{}", x))
.collect::<Vec<_>>().join(" "))).await.unwrap();
for range in [(0..20), (20..40), (40..50)] {
assert_eq!(":irc.irc 303 funny :".to_string() + &(range.map(
|x| format!("binny{0}", x))
.collect::<Vec<_>>().join(" ")),
line_stream.next().await.unwrap().unwrap());
}
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_ison_notfound() {
let (main_state, handle, port) = run_test_server(MainConfig::default()).await;
{
let mut line_stream = login_to_test_and_skip(port, "funny", "funny",
"Bunny BumBumBum").await;
line_stream.send("ISON ziggy".to_string()).await.unwrap();
assert_eq!(":irc.irc 303 funny :".to_string(),
line_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
#[tokio::test]
async fn test_command_wallops() {
let mut config = MainConfig::default();
config.operators = Some(vec![
OperatorConfig{ name: "fanny".to_string(),
password: argon2_hash_password("Funny"), mask: None },
]);
let (main_state, handle, port) = run_test_server(config).await;
{
let mut line_stream = login_to_test_and_skip(port, "funny", "funny",
"Bunny BumBumBum").await;
line_stream.send("OPER fanny Funny".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
let mut hanna_stream = login_to_test_and_skip(port, "hanna", "hanna",
"Hanna-Barbera").await;
let mut gena_stream = login_to_test_and_skip(port, "gena", "gena",
"Gena Sphinxs").await;
let mut keith_stream = login_to_test_and_skip(port, "keith", "keith",
"Keith North").await;
line_stream.send("MODE funny +w".to_string()).await.unwrap();
line_stream.next().await.unwrap().unwrap();
hanna_stream.send("MODE hanna +w".to_string()).await.unwrap();
hanna_stream.next().await.unwrap().unwrap();
gena_stream.send("MODE gena +w".to_string()).await.unwrap();
gena_stream.next().await.unwrap().unwrap();
line_stream.send("WALLOPS :Hello people".to_string()).await.unwrap();
for line_stream in [&mut line_stream, &mut hanna_stream, &mut gena_stream] {
assert_eq!(":funny!~funny@127.0.0.1 WALLOPS :Hello people".to_string(),
line_stream.next().await.unwrap().unwrap());
}
hanna_stream.send("WALLOPS :Hello people".to_string()).await.unwrap();
assert_eq!(":irc.irc 481 hanna :Permission Denied- You're not an IRC \
operator".to_string(), hanna_stream.next().await.unwrap().unwrap());
keith_stream.send("WALLOPS :Hello people".to_string()).await.unwrap();
assert_eq!(":irc.irc 481 keith :Permission Denied- You're not an IRC \
operator".to_string(), keith_stream.next().await.unwrap().unwrap());
}
quit_test_server(main_state, handle).await;
}
}