Implement responses for more error cases
Diff
src/channel.rs | 34 ++++++++++++++++++++++++++++------
src/channel/permissions.rs | 4 ++--
src/channel/response.rs | 36 ++++++++++++++++++++++++++++++++++++
src/client.rs | 15 +++++++++++----
src/connection.rs | 6 +++---
5 files changed, 80 insertions(+), 15 deletions(-)
@@ -9,7 +9,7 @@ use actix::{
};
use chrono::{DateTime, Utc};
use futures::future::Either;
use irc_proto::{Command, Message, Mode};
use irc_proto::{Command, Message, Mode, Response};
use tracing::{debug, error, info, instrument, warn, Span};
use crate::{
@@ -17,6 +17,7 @@ use crate::{
permissions::Permission,
response::{
ChannelInviteResult, ChannelJoinRejectionReason, ChannelNamesList, ChannelTopic,
MissingPrivileges,
},
},
client::Client,
@@ -137,8 +138,22 @@ impl Handler<ChannelMessage> for Channel {
};
if !self.get_user_permissions(sender.user_id).can_chatter() {
error!("User cannot send message to channel");
msg.client.do_send(Broadcast {
message: Message {
tags: None,
prefix: None,
command: Command::Response(
Response::ERR_CANNOTSENDTOCHAN,
vec![
sender.to_nick().to_string(),
self.name.to_string(),
"Cannot send to channel".to_string(),
],
),
},
span: Span::current(),
});
return;
}
@@ -421,8 +436,12 @@ impl Handler<ChannelUpdateTopic> for Channel {
.get_user_permissions(client_info.user_id)
.can_set_topic()
{
error!("User cannot set channel topic");
error!("User attempted to set channel topic without privileges");
msg.client.do_send(Broadcast {
message: MissingPrivileges(client_info.to_nick(), self.name.to_string())
.into_message(),
span: Span::current(),
});
return;
}
@@ -455,8 +474,11 @@ impl Handler<ChannelKickUser> for Channel {
};
if !self.get_user_permissions(kicker.user_id).can_kick() {
error!("Kicker can not kick people from the channel");
msg.client.do_send(Broadcast {
message: MissingPrivileges(kicker.to_nick(), self.name.to_string()).into_message(),
span: Span::current(),
});
return;
}
@@ -90,8 +90,8 @@ impl Permission {
#[must_use]
pub fn can_kick(self) -> bool {
self == Self::Operator
pub const fn can_kick(self) -> bool {
(self as i16) >= (Self::HalfOperator as i16)
}
@@ -183,3 +183,39 @@ impl ChannelInviteResult {
pub enum ChannelJoinRejectionReason {
Banned,
}
impl ChannelJoinRejectionReason {
#[must_use]
pub fn into_message(self) -> Message {
match self {
Self::Banned => Message {
tags: None,
prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
command: Command::Response(
Response::ERR_BANNEDFROMCHAN,
vec!["Cannot join channel (+b)".to_string()],
),
},
}
}
}
pub struct MissingPrivileges(pub Prefix, pub String);
impl MissingPrivileges {
#[must_use]
pub fn into_message(self) -> Message {
Message {
tags: None,
prefix: None,
command: Command::Response(
Response::ERR_CHANOPRIVSNEEDED,
vec![
self.0.to_string(),
self.1,
"You're not channel operator".to_string(),
],
),
}
}
}
@@ -15,7 +15,10 @@ use tracing::{debug, error, info, info_span, instrument, warn, Instrument, Span}
use crate::{
channel::Channel,
connection::{sasl::SaslAlreadyAuthenticated, Capability, InitiatedConnection, MessageSink},
connection::{
sasl::SaslAlreadyAuthenticated, Capability, InitiatedConnection, MessageSink,
NickNotOwnedByUser,
},
messages::{
Broadcast, ChannelFetchTopic, ChannelInvite, ChannelJoin, ChannelKickUser, ChannelList,
ChannelMemberList, ChannelMessage, ChannelPart, ChannelSetMode, ChannelUpdateTopic,
@@ -263,7 +266,8 @@ impl Handler<JoinChannelRequest> for Client {
let handle = match handle {
Ok(v) => v,
Err(error) => {
error!(?error, "Failed to join user to channel");
error!(?error, "User failed to join channel");
this.writer.write(error.into_message());
continue;
}
};
@@ -346,7 +350,10 @@ impl Handler<UserNickChangeInternal> for Client {
.into_actor(self)
.map(|res, this, ctx| {
if !res.unwrap() {
ctx.notify(Broadcast {
message: NickNotOwnedByUser(msg.new_nick).into_message(),
span: Span::current(),
});
return;
}
@@ -667,7 +674,7 @@ impl StreamHandler<Result<irc_proto::Message, ProtocolError>> for Client {
self.writer.write(Message {
tags: None,
prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
prefix: None,
command: Command::Response(
Response::RPL_TIME,
vec![
@@ -226,7 +226,7 @@ pub async fn negotiate_client_connection(
Ok(Some(initiated))
}
pub struct NickNotOwnedByUser(String);
pub struct NickNotOwnedByUser(pub String);
impl NickNotOwnedByUser {
#[must_use]
@@ -235,8 +235,8 @@ impl NickNotOwnedByUser {
tags: None,
prefix: None,
command: Command::Response(
Response::ERR_NICKLOCKED,
vec![self.0, "You must use a nick assigned to you".to_string()],
Response::ERR_NICKNAMEINUSE,
vec![self.0, "Nickname is already in use".to_string()],
),
}
}