Partial XEP-0410 support
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
parent
eba5ea9773
commit
1f4e6f6451
4 changed files with 136 additions and 5 deletions
10
src/error.rs
10
src/error.rs
|
@ -17,7 +17,7 @@ use std::error::Error as StdError;
|
|||
use std::fmt;
|
||||
|
||||
use tokio_xmpp::Error as TokioXMPPError;
|
||||
use xmpp_parsers::{FullJid, Jid};
|
||||
use xmpp_parsers::{Error as ParserError, FullJid, Jid};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
|
@ -26,6 +26,7 @@ pub enum Error {
|
|||
NonexistantSession(FullJid),
|
||||
SessionAlreadyExists(FullJid),
|
||||
XMPPError(TokioXMPPError),
|
||||
ParserError(ParserError),
|
||||
}
|
||||
|
||||
impl StdError for Error {}
|
||||
|
@ -38,6 +39,7 @@ impl fmt::Display for Error {
|
|||
Error::NonexistantSession(err) => write!(f, "Session doesn't exist: {}", err),
|
||||
Error::SessionAlreadyExists(err) => write!(f, "Session already exist: {}", err),
|
||||
Error::XMPPError(err) => write!(f, "XMPP error: {}", err),
|
||||
Error::ParserError(err) => write!(f, "Parser error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,3 +49,9 @@ impl From<TokioXMPPError> for Error {
|
|||
Error::XMPPError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParserError> for Error {
|
||||
fn from(err: ParserError) -> Error {
|
||||
Error::ParserError(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ use crate::component::ComponentTrait;
|
|||
use crate::error::Error;
|
||||
use crate::room::{Nick, Room};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ops::ControlFlow;
|
||||
use std::str::FromStr;
|
||||
|
||||
use futures::stream::StreamExt;
|
||||
use log::{debug, error};
|
||||
|
@ -28,6 +29,7 @@ use xmpp_parsers::{
|
|||
message::Message,
|
||||
muc::Muc,
|
||||
ns,
|
||||
ping::Ping,
|
||||
presence::{Presence, Type as PresenceType},
|
||||
stanza_error::{DefinedCondition, ErrorType, StanzaError},
|
||||
BareJid, Element, Jid,
|
||||
|
@ -43,6 +45,7 @@ async fn handle_iq_disco<C: ComponentTrait>(
|
|||
let identities = vec![Identity::new("conference", "text", "en", "Hanabi")];
|
||||
let features = vec![
|
||||
Feature::new("http://jabber.org/protocol/disco#info"),
|
||||
Feature::new("http://jabber.org/protocol/muc#self-ping-optimization"),
|
||||
Feature::new("xmpp:bouah.net:hanabi:muc:0"),
|
||||
];
|
||||
let extensions = Vec::new();
|
||||
|
@ -75,11 +78,88 @@ async fn handle_iq_disco<C: ComponentTrait>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_iq<C: ComponentTrait>(component: &mut C, iq: Iq) -> Result<(), Error> {
|
||||
async fn handle_iq_ping<C: ComponentTrait>(
|
||||
component: &mut C,
|
||||
iq: Iq,
|
||||
payload: Element,
|
||||
rooms: &mut HashMap<BareJid, Room>,
|
||||
) -> Result<(), Error> {
|
||||
match Ping::try_from(payload) {
|
||||
Ok(_) => {
|
||||
let success = Iq::empty_result(iq.from.as_ref().unwrap().clone(), iq.id.clone());
|
||||
let from = iq.from.unwrap();
|
||||
let to = iq.to.unwrap();
|
||||
|
||||
// Pinging a participant
|
||||
if let Jid::Full(participant) = to.clone() {
|
||||
// TODO: Reply from participant if joined and nick is correct
|
||||
let bare = BareJid::from(to.clone());
|
||||
if let Some(room) = rooms.get(&bare) {
|
||||
if room.is_joined(&participant) &&
|
||||
let Some(occupant) = room.occupants.get(&participant.resource) {
|
||||
if occupant.contains(&from) {
|
||||
let success = success.clone().with_from(Jid::Full(participant));
|
||||
component.send_stanza(success).await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
} else if let Jid::Bare(to) = to.clone() {
|
||||
// Pinging the component
|
||||
if to.node.is_none() {
|
||||
let domain = BareJid::from_str(&to.domain).unwrap();
|
||||
let success = success.clone().with_from(Jid::Bare(domain));
|
||||
component.send_stanza(success).await?;
|
||||
// Pinging a room
|
||||
} else {
|
||||
if let Some(room) = rooms.get(&to) {
|
||||
if room.is_joined(&from) {
|
||||
let success = success.clone().with_from(Jid::Bare(to));
|
||||
component.send_stanza(success).await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let success = success.clone().with_from(Jid::Bare(to));
|
||||
component.send_stanza(success).await?;
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let error = Iq::from_error(
|
||||
iq.id,
|
||||
StanzaError {
|
||||
type_: ErrorType::Modify,
|
||||
defined_condition: DefinedCondition::NotAcceptable,
|
||||
by: Some(Jid::Bare(BareJid::from(to))),
|
||||
texts: BTreeMap::new(),
|
||||
other: None,
|
||||
},
|
||||
);
|
||||
component.send_stanza(error).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to parse iq as ping: {}", err);
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_iq<C: ComponentTrait>(
|
||||
component: &mut C,
|
||||
iq: Iq,
|
||||
rooms: &mut HashMap<BareJid, Room>,
|
||||
) -> Result<(), Error> {
|
||||
match iq.payload {
|
||||
IqType::Get(ref payload) => {
|
||||
if payload.is("query", ns::DISCO_INFO) {
|
||||
handle_iq_disco(component, iq.clone(), payload.clone()).await?
|
||||
} else if payload.is("ping", ns::PING) {
|
||||
handle_iq_ping(component, iq.clone(), payload.clone(), rooms).await?
|
||||
} else {
|
||||
// We MUST answer unhandled get iqs with a service-unavailable error.
|
||||
let error = StanzaError::new(
|
||||
|
@ -209,7 +289,7 @@ pub async fn handle_stanza<C: ComponentTrait>(
|
|||
debug!("RECV {}", String::from(&elem));
|
||||
if elem.is("iq", ns::COMPONENT_ACCEPT) {
|
||||
let iq = Iq::try_from(elem).unwrap();
|
||||
handle_iq(component, iq).await?;
|
||||
handle_iq(component, iq, rooms).await?;
|
||||
} else if elem.is("message", ns::COMPONENT_ACCEPT) {
|
||||
let message = Message::try_from(elem).unwrap();
|
||||
handle_message(component, message).await?;
|
||||
|
|
|
@ -310,6 +310,10 @@ impl Room {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_joined<J: Into<Jid>>(&self, _jid: &J) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -367,6 +371,10 @@ impl Occupant {
|
|||
Err(Error::NonexistantSession(real))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains<J: Into<Jid>>(&self, _jid: &J) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Occupant {
|
||||
|
|
|
@ -16,12 +16,16 @@ use crate::component::TestComponent;
|
|||
use crate::handlers::handle_stanza;
|
||||
use crate::room::Room;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::str::FromStr;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use xmpp_parsers::{
|
||||
disco::{DiscoInfoQuery, DiscoInfoResult, Feature},
|
||||
iq::{Iq, IqType},
|
||||
muc::Muc,
|
||||
ping::Ping,
|
||||
presence::{Presence, Type as PresenceType},
|
||||
stanza_error::{DefinedCondition, ErrorType, StanzaError},
|
||||
BareJid, Element, FullJid, Jid,
|
||||
};
|
||||
|
@ -62,3 +66,34 @@ async fn test_iq_unimplemented() {
|
|||
component.expect(reply);
|
||||
handle_stanza(&mut component, &mut rooms).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_self_ping_participant_non_existing() {
|
||||
let realjid1 = Jid::Full(FullJid::from_str("foo@bar/qxx").unwrap());
|
||||
let roomjid = COMPONENT_JID.clone().with_node("room");
|
||||
let participant1 = roomjid.clone().with_resource("nick1");
|
||||
|
||||
let ping: Element = Iq {
|
||||
from: Some(realjid1.clone()),
|
||||
to: Some(Jid::Full(participant1.clone())),
|
||||
id: String::from("ping"),
|
||||
payload: IqType::Get(Ping {}.into()),
|
||||
}
|
||||
.into();
|
||||
|
||||
let mut component = TestComponent::new(vec![ping]);
|
||||
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
|
||||
|
||||
component.expect(Into::<Element>::into(Iq::from_error(
|
||||
"ping",
|
||||
StanzaError {
|
||||
type_: ErrorType::Modify,
|
||||
defined_condition: DefinedCondition::NotAcceptable,
|
||||
by: Some(Jid::Bare(roomjid)),
|
||||
texts: BTreeMap::new(),
|
||||
other: None,
|
||||
},
|
||||
)));
|
||||
|
||||
handle_stanza(&mut component, &mut rooms).await.unwrap();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue