diff --git a/src/protos/xmpp/bind.rs b/src/protos/xmpp/bind.rs index c790b0c..0b5b1e1 100644 --- a/src/protos/xmpp/bind.rs +++ b/src/protos/xmpp/bind.rs @@ -29,6 +29,28 @@ impl Jid { pub fn to_string(&self) -> String { format!("{}@{}/{}", self.name.0, self.server.0, self.resource.0) } + + pub fn from_string(i: &str) -> Result { + // TODO make regex static + use regex::Regex; + let re = Regex::new(r"^([a-zA-Z]+)@([a-zA-Z.]+)/([a-zA-Z\-]+)$").unwrap(); + let m = re + .captures(i) + .ok_or(ffail!("Incorrectly format jid: {i}"))?; + + let name = m.get(1).unwrap(); + let name = Name(name.as_str().to_string()); + let server = m.get(2).unwrap(); + let server = Server(server.as_str().to_string()); + let resource = m.get(3).unwrap(); + let resource = Resource(resource.as_str().to_string()); + + Ok(Jid { + name, + server, + resource, + }) + } } /// Request to bind to a resource. diff --git a/src/protos/xmpp/disco.rs b/src/protos/xmpp/disco.rs new file mode 100644 index 0000000..bd9ead2 --- /dev/null +++ b/src/protos/xmpp/disco.rs @@ -0,0 +1,289 @@ +use quick_xml::events::Event; +use quick_xml::name::ResolveResult; + +use crate::prelude::*; +use crate::util::xml::*; + +use super::bind::Jid; + +pub const XMLNS_INFO: &'static str = "http://jabber.org/protocol/disco#info"; +pub const XMLNS_ITEM: &'static str = "http://jabber.org/protocol/disco#item"; + + +pub struct InfoQuery { + pub node: Option, + pub identity: Vec, + pub feature: Vec, +} + +impl FromXml for InfoQuery { + type P = impl Parser>; + + fn parse() -> Self::P { + |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { + let mut node = None; + let mut identity = vec![]; + let mut feature = vec![]; + let (bytes, end) = match event { + Event::Start(bytes) => (bytes, false), + Event::Empty(bytes) => (bytes, true), + _ => return Err(ffail!("Unexpected XML event: {event:?}")), + }; + for attr in bytes.attributes() { + let attr = attr?; + match attr.key.0 { + b"node" => { + let s = std::str::from_utf8(bytes)?; + node = Some(s.to_owned()) + } + _ => {} + } + } + if end { + return Ok(InfoQuery { node, identity, feature }) + } + loop { + let (namespace, event) = yield; + let bytes = match event { + Event::Start(bytes) => bytes, + Event::Empty(bytes) => bytes, + Event::End(_) => break, + _ => return Err(ffail!("Unexpected XML event: {event:?}")), + }; + if bytes.name().0 == Identity::NAME.as_bytes() { + let res = delegate_parsing!(Identity, namespace, event)?; + identity.push(res); + } else if bytes.name().0 == Feature::NAME.as_bytes() { + let res = delegate_parsing!(Feature, namespace, event)?; + feature.push(res); + } else { + return Err(ffail!("Unexpected XML event: {event:?}")); + } + } + return Ok(InfoQuery { node, identity, feature }) + } + } +} + +impl FromXmlTag for InfoQuery { + const NAME: &'static str = "query"; + + const NS: &'static str = XMLNS_INFO; +} + +pub struct Identity { + category: String, + name: Option, + r#type: String, +} + +impl FromXml for Identity { + type P = impl Parser>; + + fn parse() -> Self::P { + |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { + let mut category = None; + let mut name = None; + let mut r#type = None; + let (bytes, end) = match event { + Event::Start(bytes) => (bytes, false), + Event::Empty(bytes) => (bytes, true), + _ => return Err(ffail!("Unexpected XML event: {event:?}")), + }; + for attr in bytes.attributes() { + let attr = attr?; + match attr.key.0 { + b"category" => { + let s = std::str::from_utf8(bytes)?; + category = Some(s.to_owned()) + } + b"name" => { + let s = std::str::from_utf8(bytes)?; + name = Some(s.to_owned()) + } + b"type" => { + let s = std::str::from_utf8(bytes)?; + r#type = Some(s.to_owned()) + } + _ => {} + } + } + let Some(category) = category else { + return Err(ffail!("No jid provided")); + }; + let Some(r#type) = r#type else { + return Err(ffail!("No type provided")); + }; + let item = Identity { category, name, r#type }; + if end { + return Ok(item); + } + + let (namespace, event) = yield; + let Event::End(bytes) = event else { + return Err(ffail!("Unexpected XML event: {event:?}")); + }; + Ok(item) + } + } +} + +impl FromXmlTag for Identity { + const NAME: &'static str = "identity"; + + const NS: &'static str = XMLNS_INFO; +} + +pub struct Feature { + pub var: String, +} + +impl FromXml for Feature { + type P = impl Parser>; + + fn parse() -> Self::P { + |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { + let mut var = None; + let (bytes, end) = match event { + Event::Start(bytes) => (bytes, false), + Event::Empty(bytes) => (bytes, true), + _ => return Err(ffail!("Unexpected XML event: {event:?}")), + }; + for attr in bytes.attributes() { + let attr = attr?; + match attr.key.0 { + b"var" => { + let s = std::str::from_utf8(bytes)?; + var = Some(s.to_owned()) + } + _ => {} + } + } + let Some(var) = var else { + return Err(ffail!("No jid provided")); + }; + let item = Feature { var }; + if end { + return Ok(item); + } + + let (namespace, event) = yield; + let Event::End(bytes) = event else { + return Err(ffail!("Unexpected XML event: {event:?}")); + }; + Ok(item) + } + } +} + +impl FromXmlTag for Feature { + const NAME: &'static str = "feature"; + + const NS: &'static str = XMLNS_INFO; +} + +pub struct ItemQuery { + pub item: Vec, +} + +impl FromXml for ItemQuery { + type P = impl Parser>; + + fn parse() -> Self::P { + |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { + let mut item = vec![]; + let (bytes, end) = match event { + Event::Start(bytes) => (bytes, false), + Event::Empty(bytes) => (bytes, true), + _ => return Err(ffail!("Unexpected XML event: {event:?}")), + }; + if end { + return Ok(ItemQuery { item }) + } + loop { + let (namespace, event) = yield; + let bytes = match event { + Event::Start(bytes) => bytes, + Event::Empty(bytes) => bytes, + Event::End(_) => break, + _ => return Err(ffail!("Unexpected XML event: {event:?}")), + }; + if bytes.name().0 == Item::NAME.as_bytes() { + let res = delegate_parsing!(Item, namespace, event)?; + item.push(res); + } else { + return Err(ffail!("Unexpected XML event: {event:?}")); + } + } + Ok(ItemQuery { item }) + } + } +} + +impl FromXmlTag for ItemQuery { + const NAME: &'static str = "query"; + + const NS: &'static str = XMLNS_ITEM; +} + +#[derive(Debug)] +pub struct Item { + pub jid: super::bind::Jid, + pub name: Option, + pub node: Option, +} + +impl FromXml for Item { + type P = impl Parser>; + + fn parse() -> Self::P { + |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { + let mut jid = None; + let mut name = None; + let mut node = None; + let (bytes, end) = match event { + Event::Start(bytes) => (bytes, false), + Event::Empty(bytes) => (bytes, true), + _ => return Err(ffail!("Unexpected XML event: {event:?}")), + }; + for attr in bytes.attributes() { + let attr = attr?; + match attr.key.0 { + b"name" => { + let s = std::str::from_utf8(bytes)?; + name = Some(s.to_owned()) + } + b"node" => { + let s = std::str::from_utf8(bytes)?; + node = Some(s.to_owned()) + } + b"jid" => { + let s = std::str::from_utf8(bytes)?; + let s = Jid::from_string(s)?; + jid = Some(s) + } + _ => {} + } + } + let Some(jid) = jid else { + return Err(ffail!("No jid provided")); + }; + let item = Item { jid, name, node }; + if end { + return Ok(item); + } + + let (namespace, event) = yield; + let Event::End(bytes) = event else { + return Err(ffail!("Unexpected XML event: {event:?}")); + }; + Ok(item) + } + } +} + +impl FromXmlTag for Item { + const NAME: &'static str = "item"; + + const NS: &'static str = XMLNS_ITEM; +} \ No newline at end of file diff --git a/src/protos/xmpp/mod.rs b/src/protos/xmpp/mod.rs index 8e46e8d..0f99af7 100644 --- a/src/protos/xmpp/mod.rs +++ b/src/protos/xmpp/mod.rs @@ -1,5 +1,6 @@ pub mod bind; pub mod client; +pub mod disco; pub mod muc; pub mod roster; pub mod sasl;