forked from lavina/lavina
Compare commits
2 Commits
ad00fe759a
...
ed16b6765f
Author | SHA1 | Date |
---|---|---|
homycdev | ed16b6765f | |
Nikita Vilunov | a87f7c9d73 |
|
@ -8,7 +8,6 @@
|
|||
//! A player actor is a serial handler of commands from a single player. It is preferable to run all per-player validations in the player actor,
|
||||
//! so that they don't overload the room actor.
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
|
@ -125,15 +124,6 @@ impl PlayerConnection {
|
|||
self.player_handle.send(ActorCommand::ClientCommand(cmd, self.connection_id.clone())).await;
|
||||
Ok(deferred.await?)
|
||||
}
|
||||
|
||||
/// Handler in [Player::check_user_existance].
|
||||
#[tracing::instrument(skip(self), name = "PlayerConnection::send_dialog_message")]
|
||||
pub async fn check_user_existance(&self, recipient: PlayerId) -> Result<GetInfoResult> {
|
||||
let (promise, deferred) = oneshot();
|
||||
let cmd = ClientCommand::GetInfo { recipient, promise };
|
||||
self.player_handle.send(ActorCommand::ClientCommand(cmd, self.connection_id.clone())).await;
|
||||
Ok(deferred.await?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a player actor.
|
||||
|
@ -210,15 +200,6 @@ pub enum ClientCommand {
|
|||
body: Str,
|
||||
promise: Promise<()>,
|
||||
},
|
||||
GetInfo {
|
||||
recipient: PlayerId,
|
||||
promise: Promise<GetInfoResult>,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum GetInfoResult {
|
||||
UserExists,
|
||||
UserDoesntExist,
|
||||
}
|
||||
|
||||
pub enum JoinResult {
|
||||
|
@ -519,10 +500,6 @@ impl Player {
|
|||
self.send_dialog_message(connection_id, recipient, body).await;
|
||||
let _ = promise.send(());
|
||||
}
|
||||
ClientCommand::GetInfo { recipient, promise } => {
|
||||
let result = self.check_user_existance(recipient).await;
|
||||
let _ = promise.send(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -619,15 +596,6 @@ impl Player {
|
|||
self.broadcast_update(update, connection_id).await;
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self), name = "Player::check_user_existance")]
|
||||
async fn check_user_existance(&self, recipient: PlayerId) -> GetInfoResult {
|
||||
if self.storage.check_user_existance(recipient.as_inner().as_ref()).await.unwrap() {
|
||||
GetInfoResult::UserExists
|
||||
} else {
|
||||
GetInfoResult::UserDoesntExist
|
||||
}
|
||||
}
|
||||
|
||||
/// Broadcasts an update to all connections except the one with the given id.
|
||||
///
|
||||
/// This is called after handling a client command.
|
||||
|
|
|
@ -7,7 +7,7 @@ use anyhow::anyhow;
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::Deserialize;
|
||||
use sqlx::sqlite::SqliteConnectOptions;
|
||||
use sqlx::{ConnectOptions, Connection, Execute, FromRow, Sqlite, SqliteConnection, Transaction};
|
||||
use sqlx::{ConnectOptions, Connection, FromRow, Sqlite, SqliteConnection, Transaction};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
@ -56,17 +56,6 @@ impl Storage {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self), name = "Storage::check_user_existance")]
|
||||
pub async fn check_user_existance(&self, username: &str) -> Result<bool> {
|
||||
let mut executor = self.conn.lock().await;
|
||||
let result: Option<(String,)> = sqlx::query_as("select name from users where name = ?;")
|
||||
.bind(username)
|
||||
.fetch_optional(&mut *executor)
|
||||
.await?;
|
||||
|
||||
Ok(result.is_some())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self), name = "Storage::retrieve_room_by_name")]
|
||||
pub async fn retrieve_room_by_name(&self, name: &str) -> Result<Option<StoredRoom>> {
|
||||
let mut executor = self.conn.lock().await;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
use lavina_core::{prelude::Str, repo::Storage};
|
||||
use std::future::Future;
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
pub mod whois;
|
||||
|
||||
pub struct HandlerArgs<T: AsyncWrite + Unpin> {
|
||||
server_name: Str,
|
||||
client: Str,
|
||||
writer: T,
|
||||
storage: Storage,
|
||||
}
|
||||
|
||||
impl<T> HandlerArgs<T>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
pub fn new(server_name: Str, client: Str, writer: T, storage: Storage) -> HandlerArgs<T> {
|
||||
HandlerArgs {
|
||||
server_name,
|
||||
client,
|
||||
writer,
|
||||
storage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Handler<T>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
fn handle(&self, arg: HandlerArgs<T>) -> impl Future<Output = anyhow::Result<()>>;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
|
||||
use crate::{prelude::Str, response::WriteResponse};
|
||||
use lavina_core::prelude::Str;
|
||||
use proto_irc::response::WriteResponse;
|
||||
|
||||
/// ErrNoSuchNick401
|
||||
pub struct ErrNoSuchNick401 {
|
||||
|
@ -33,7 +34,7 @@ impl ErrNoNicknameGiven431 {
|
|||
}
|
||||
|
||||
impl WriteResponse for ErrNoSuchNick401 {
|
||||
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"401 ").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
|
@ -45,7 +46,7 @@ impl WriteResponse for ErrNoSuchNick401 {
|
|||
}
|
||||
|
||||
impl WriteResponse for ErrNoNicknameGiven431 {
|
||||
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"431").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" :").await?;
|
||||
|
@ -55,7 +56,7 @@ impl WriteResponse for ErrNoNicknameGiven431 {
|
|||
}
|
||||
|
||||
impl WriteResponse for ErrNoSuchServer402 {
|
||||
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"402 ").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
|
@ -0,0 +1,70 @@
|
|||
use tokio::io::AsyncWrite;
|
||||
|
||||
use lavina_core::prelude::Str;
|
||||
use proto_irc::client::command_args::Whois;
|
||||
use proto_irc::response::{IrcResponseMessage, WriteResponse};
|
||||
|
||||
use crate::commands::whois::error::{ErrNoNicknameGiven431, ErrNoSuchNick401};
|
||||
use crate::commands::whois::response::{RplEndOfWhois318, RplWhoIsUser311};
|
||||
use crate::commands::Handler;
|
||||
|
||||
use super::HandlerArgs;
|
||||
|
||||
pub mod error;
|
||||
pub mod response;
|
||||
|
||||
impl<T: AsyncWrite + Unpin> Handler<T> for Whois {
|
||||
async fn handle(&self, body: HandlerArgs<T>) -> anyhow::Result<()> {
|
||||
match self {
|
||||
Whois::Nick(nick) => handle_nick_target(nick.clone(), body).await?,
|
||||
Whois::TargetNick(_, nick) => handle_nick_target(nick.clone(), body).await?,
|
||||
Whois::EmptyArgs => {
|
||||
let HandlerArgs {
|
||||
server_name,
|
||||
mut writer,
|
||||
..
|
||||
} = body;
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
ErrNoNicknameGiven431::new(server_name.clone()),
|
||||
)
|
||||
.write_response(&mut writer)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_nick_target(nick: Str, body: HandlerArgs<impl AsyncWrite + Unpin>) -> anyhow::Result<()> {
|
||||
let HandlerArgs {
|
||||
server_name,
|
||||
mut writer,
|
||||
client,
|
||||
mut storage,
|
||||
} = body;
|
||||
if let Some(user) = storage.retrieve_user_by_name(nick.clone().as_ref()).await? {
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
RplWhoIsUser311::new(
|
||||
client.clone(),
|
||||
nick.clone(),
|
||||
Some(Str::from(user.name.clone())),
|
||||
server_name.clone(),
|
||||
nick.clone(),
|
||||
),
|
||||
)
|
||||
.write_response(&mut writer)
|
||||
.await?;
|
||||
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
RplEndOfWhois318::new(client.clone(), user.name.clone().into()),
|
||||
)
|
||||
.write_response(&mut writer)
|
||||
.await?
|
||||
} else {
|
||||
ErrNoSuchNick401::new(client.clone(), nick.clone()).write_response(&mut writer).await?
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -2,7 +2,8 @@ use std::{net::IpAddr, time::SystemTime};
|
|||
|
||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
|
||||
use crate::{prelude::Str, response::WriteResponse, Chan};
|
||||
use lavina_core::prelude::Str;
|
||||
use proto_irc::{response::WriteResponse, Chan};
|
||||
|
||||
/// "<client> <nick> :has client certificate fingerprint <fingerprint>"
|
||||
/// Clients MUST only be sent this numeric if they are either using the WHOIS command on themselves
|
||||
|
@ -149,12 +150,12 @@ impl RplEndOfWhois318 {
|
|||
}
|
||||
|
||||
impl WriteResponse for RplWhoIsUser311 {
|
||||
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"311 ").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(self.nick.as_bytes()).await?;
|
||||
if let Some(username) = &self.username {
|
||||
if let Some(username) = self.username {
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(username.as_bytes()).await?;
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ impl WriteResponse for RplWhoIsUser311 {
|
|||
}
|
||||
|
||||
impl WriteResponse for RplEndOfWhois318 {
|
||||
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"318 ").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
|
@ -1,18 +0,0 @@
|
|||
use lavina_core::{player::PlayerConnection, prelude::Str, LavinaCore};
|
||||
use std::future::Future;
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
pub struct IrcConnection<'a, T: AsyncWrite + Unpin> {
|
||||
pub server_name: Str,
|
||||
/// client is nick of requester
|
||||
pub client: Str,
|
||||
pub writer: &'a mut T,
|
||||
pub player_connection: &'a mut PlayerConnection,
|
||||
}
|
||||
|
||||
pub trait Handler<T>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
fn handle(&self, arg: IrcConnection<T>) -> impl Future<Output = anyhow::Result<()>>;
|
||||
}
|
|
@ -29,12 +29,11 @@ use proto_irc::user::PrefixedNick;
|
|||
use proto_irc::{Chan, Recipient, Tag};
|
||||
use sasl::AuthBody;
|
||||
mod cap;
|
||||
use handler::Handler;
|
||||
mod whois;
|
||||
mod commands;
|
||||
|
||||
use crate::cap::Capabilities;
|
||||
|
||||
mod handler;
|
||||
use commands::Handler;
|
||||
use proto_irc::response::IrcResponseMessage;
|
||||
|
||||
pub const APP_VERSION: &str = concat!("lavina", "_", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
|
@ -81,7 +80,7 @@ async fn handle_socket(
|
|||
match registered_user {
|
||||
Ok(user) => {
|
||||
log::debug!("User registered");
|
||||
handle_registered_socket(config, core.players, core.rooms, &mut reader, &mut writer, user, &mut storage).await?;
|
||||
handle_registered_socket(config, core.players, core.rooms, &mut reader, &mut writer, user).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!("Registration failed: {err}");
|
||||
|
@ -812,15 +811,13 @@ async fn handle_incoming_message(
|
|||
}
|
||||
},
|
||||
ClientMessage::Whois { arg } => {
|
||||
arg.handle(handler::IrcConnection {
|
||||
server_name: config.server_name.clone(),
|
||||
client: user.nickname.clone(),
|
||||
arg.handle(commands::HandlerArgs::new(
|
||||
config.server_name.clone(),
|
||||
user.nickname.clone(),
|
||||
writer,
|
||||
player_connection: user_handle,
|
||||
})
|
||||
.await?;
|
||||
|
||||
writer.flush().await?;
|
||||
storage.clone(),
|
||||
))
|
||||
.await?
|
||||
}
|
||||
ClientMessage::Mode { target } => {
|
||||
match target {
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
use lavina_core::{
|
||||
player::{GetInfoResult, PlayerId},
|
||||
prelude::Str,
|
||||
};
|
||||
use proto_irc::{
|
||||
client::command_args::Whois,
|
||||
commands::whois::{
|
||||
error::{ErrNoNicknameGiven431, ErrNoSuchNick401},
|
||||
response::RplEndOfWhois318,
|
||||
},
|
||||
response::{IrcResponseMessage, WriteResponse},
|
||||
};
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
use crate::handler::{Handler, IrcConnection};
|
||||
|
||||
impl<T: AsyncWrite + Unpin> Handler<T> for Whois {
|
||||
async fn handle(&self, body: IrcConnection<'_, T>) -> anyhow::Result<()> {
|
||||
match self {
|
||||
Whois::Nick(nick) => handle_nick_target(nick.clone(), body).await?,
|
||||
Whois::TargetNick(_, nick) => handle_nick_target(nick.clone(), body).await?,
|
||||
Whois::EmptyArgs => {
|
||||
let IrcConnection {
|
||||
server_name,
|
||||
mut writer,
|
||||
..
|
||||
} = body;
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
ErrNoNicknameGiven431::new(server_name.clone()),
|
||||
)
|
||||
.write_response(&mut writer)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_nick_target(nick: Str, body: IrcConnection<'_, impl AsyncWrite + Unpin>) -> anyhow::Result<()> {
|
||||
let IrcConnection {
|
||||
server_name,
|
||||
mut writer,
|
||||
client,
|
||||
player_connection,
|
||||
} = body;
|
||||
|
||||
if let GetInfoResult::UserDoesntExist =
|
||||
player_connection.check_user_existance(PlayerId::from(nick.clone())?).await?
|
||||
{
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
ErrNoSuchNick401::new(client.clone(), nick.clone()),
|
||||
)
|
||||
.write_response(&mut writer)
|
||||
.await?
|
||||
}
|
||||
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
RplEndOfWhois318::new(client.clone(), nick.clone()),
|
||||
)
|
||||
.write_response(&mut writer)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -383,14 +383,7 @@ async fn scenario_two_users() -> Result<()> {
|
|||
// The second user should receive the PART message
|
||||
s2.expect(":tester1 PART #test").await?;
|
||||
|
||||
s1.send("WHOIS tester2").await?;
|
||||
s1.expect(":testserver 318 tester1 tester2 :End of /WHOIS list").await?;
|
||||
|
||||
stream1.shutdown().await?;
|
||||
s2.send("WHOIS tester3").await?;
|
||||
s2.expect(":testserver 401 tester2 tester3 :No such nick/channel").await?;
|
||||
s2.expect(":testserver 318 tester2 tester3 :End of /WHOIS list").await?;
|
||||
|
||||
stream2.shutdown().await?;
|
||||
|
||||
server.shutdown().await?;
|
||||
|
|
|
@ -55,19 +55,13 @@ impl<'a> TestScope<'a> {
|
|||
Ok(event)
|
||||
}
|
||||
|
||||
async fn read<T: FromXml>(&mut self) -> Result<T> {
|
||||
self.buffer.clear();
|
||||
let (ns, event) = self.reader.read_resolved_event_into_async(&mut self.buffer).await?;
|
||||
let mut parser: Continuation<_, std::result::Result<T, anyhow::Error>> = T::parse().consume(ns, &event);
|
||||
loop {
|
||||
match parser {
|
||||
Continuation::Final(res) => return Ok(res?),
|
||||
Continuation::Continue(next) => {
|
||||
let (ns, event) = self.reader.read_resolved_event_into_async(&mut self.buffer).await?;
|
||||
parser = next.consume(ns, &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn expect_starttls_required(&mut self) -> Result<()> {
|
||||
assert_matches!(self.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"required"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,6 +92,24 @@ impl<'a> TestScopeTls<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn expect_auth_mechanisms(&mut self) -> Result<()> {
|
||||
assert_matches!(self.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"mechanisms"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"mechanism"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::Text(b) => assert_eq!(&*b, b"PLAIN"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"mechanism"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"mechanisms"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn expect_bind_feature(&mut self) -> Result<()> {
|
||||
assert_matches!(self.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"bind"));
|
||||
assert_matches!(self.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn next_xml_event(&mut self) -> Result<Event<'_>> {
|
||||
self.buffer.clear();
|
||||
let event = self.reader.read_event_into_async(&mut self.buffer);
|
||||
|
@ -176,11 +188,7 @@ async fn scenario_basic() -> Result<()> {
|
|||
s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"required"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
s.expect_starttls_required().await?;
|
||||
s.send(r#"<starttls/>"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"proceed"));
|
||||
let buffer = s.buffer;
|
||||
|
@ -202,14 +210,7 @@ async fn scenario_basic() -> Result<()> {
|
|||
s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
|
||||
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"mechanisms"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"mechanism"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Text(b) => assert_eq!(&*b, b"PLAIN"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"mechanism"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"mechanisms"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
s.expect_auth_mechanisms().await?;
|
||||
|
||||
// base64-encoded b"\x00tester\x00password"
|
||||
s.send(r#"<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AHRlc3RlcgBwYXNzd29yZA==</auth>"#)
|
||||
|
@ -219,9 +220,7 @@ async fn scenario_basic() -> Result<()> {
|
|||
s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"bind"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
s.expect_bind_feature().await?;
|
||||
s.send(r#"<iq id="bind_1" type="set"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>kek</resource></bind></iq>"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"iq"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"bind"));
|
||||
|
@ -257,11 +256,7 @@ async fn scenario_wrong_password() -> Result<()> {
|
|||
s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"required"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
s.expect_starttls_required().await?;
|
||||
s.send(r#"<starttls/>"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"proceed"));
|
||||
let buffer = s.buffer;
|
||||
|
@ -283,15 +278,7 @@ async fn scenario_wrong_password() -> Result<()> {
|
|||
s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
|
||||
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"mechanisms"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"mechanism"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Text(b) => assert_eq!(&*b, b"PLAIN"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"mechanism"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"mechanisms"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
|
||||
s.expect_auth_mechanisms().await?;
|
||||
// base64-encoded b"\x00tester\x00password2"
|
||||
s.send(r#"<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AHRlc3RlcgBwYXNzd29yZDI=</auth>"#)
|
||||
.await?;
|
||||
|
@ -323,11 +310,7 @@ async fn scenario_basic_without_headers() -> Result<()> {
|
|||
s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"required"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
s.expect_starttls_required().await?;
|
||||
s.send(r#"<starttls/>"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"proceed"));
|
||||
let buffer = s.buffer;
|
||||
|
@ -375,11 +358,7 @@ async fn terminate_socket() -> Result<()> {
|
|||
s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"required"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"starttls"));
|
||||
assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features"));
|
||||
s.expect_starttls_required().await?;
|
||||
s.send(r#"<starttls/>"#).await?;
|
||||
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"proceed"));
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
pub mod whois;
|
|
@ -1,2 +0,0 @@
|
|||
pub mod error;
|
||||
pub mod response;
|
|
@ -1,6 +1,5 @@
|
|||
//! Client-to-Server IRC protocol.
|
||||
pub mod client;
|
||||
pub mod commands;
|
||||
mod prelude;
|
||||
pub mod response;
|
||||
pub mod server;
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::prelude::Str;
|
|||
use crate::Tag;
|
||||
|
||||
pub trait WriteResponse {
|
||||
fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> impl Future<Output = std::io::Result<()>>;
|
||||
fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> impl Future<Output = std::io::Result<()>>;
|
||||
}
|
||||
|
||||
/// Server-to-client enum agnostic message
|
||||
|
@ -34,7 +34,7 @@ impl<T> IrcResponseMessage<T> {
|
|||
}
|
||||
|
||||
impl<T: WriteResponse> WriteResponse for IrcResponseMessage<T> {
|
||||
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
if let Some(sender) = &self.sender {
|
||||
writer.write_all(b":").await?;
|
||||
writer.write_all(sender.as_bytes()).await?;
|
||||
|
|
Loading…
Reference in New Issue