//! Handling of all client2server iq stanzas use quick_xml::events::Event; use lavina_core::room::RoomId; use lavina_core::LavinaCore; use proto_xmpp::bind::{BindRequest, BindResponse, Jid, Name, Server}; use proto_xmpp::client::{Iq, IqError, IqErrorCondition, IqErrorType, IqType}; use proto_xmpp::disco::{Feature, Identity, InfoQuery, Item, ItemQuery}; use proto_xmpp::mam::{Fin, Set}; use proto_xmpp::roster::RosterQuery; use proto_xmpp::session::Session; use proto_xmpp::xml::ToXml; use crate::proto::IqClientBody; use crate::XmppConnection; impl<'a> XmppConnection<'a> { pub async fn handle_iq(&self, output: &mut Vec>, iq: Iq) { match iq.body { IqClientBody::Bind(req) => { let req = Iq { from: None, id: iq.id, to: None, r#type: IqType::Result, body: self.bind(&req).await, }; req.serialize(output); } IqClientBody::Session(_) => { let req = Iq { from: None, id: iq.id, to: None, r#type: IqType::Result, body: Session, }; req.serialize(output); } IqClientBody::Roster(_) => { let req = Iq { from: None, id: iq.id, to: None, r#type: IqType::Result, body: RosterQuery, }; req.serialize(output); } IqClientBody::DiscoInfo(info) => { let response = self.disco_info(iq.to.as_ref(), &info).await; match response { Ok(response) => { let req = Iq { from: iq.to, id: iq.id, to: None, r#type: IqType::Result, body: response, }; req.serialize(output); } Err(response) => { let req = Iq { from: iq.to, id: iq.id, to: None, r#type: IqType::Error, body: response, }; req.serialize(output); } } } IqClientBody::DiscoItem(item) => { let response = self.disco_items(iq.to.as_ref(), &item, self.core).await; let req = Iq { from: iq.to, id: iq.id, to: None, r#type: IqType::Result, body: response, }; req.serialize(output); } IqClientBody::MessageArchiveRequest(_) => { let response = Iq { from: iq.to, id: iq.id, to: None, r#type: IqType::Result, body: Fin { set: Set { count: Some(0) }, }, }; response.serialize(output); } _ => { let req = Iq { from: None, id: iq.id, to: None, r#type: IqType::Error, body: IqError { r#type: IqErrorType::Cancel, condition: None, }, }; req.serialize(output); } } } pub(crate) async fn bind(&self, req: &BindRequest) -> BindResponse { BindResponse(Jid { name: Some(self.user.xmpp_name.clone()), server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }) } async fn disco_info(&self, to: Option<&Jid>, req: &InfoQuery) -> Result { let identity; let feature; match to { Some(Jid { name: None, server, resource: None, }) if server.0 == self.hostname => { identity = vec![Identity { category: "server".into(), name: None, r#type: "im".into(), }]; feature = vec![ Feature::new("http://jabber.org/protocol/disco#info"), Feature::new("http://jabber.org/protocol/disco#items"), Feature::new("iq"), Feature::new("presence"), ] } Some(Jid { name: None, server, resource: None, }) if server.0 == self.hostname_rooms => { identity = vec![Identity { category: "conference".into(), name: Some("Chat rooms".into()), r#type: "text".into(), }]; feature = vec![ Feature::new("http://jabber.org/protocol/disco#info"), Feature::new("http://jabber.org/protocol/disco#items"), Feature::new("http://jabber.org/protocol/muc"), ] } Some(Jid { name: Some(room_name), server, resource: None, }) if server.0 == self.hostname_rooms => { let room_id = RoomId::from(room_name.0.clone()).unwrap(); let Some(_) = self.core.get_room(&room_id).await else { // TODO should return item-not-found // example: // // // Conference room does not exist // return Err(IqError { r#type: IqErrorType::Cancel, condition: Some(IqErrorCondition::ItemNotFound), }); }; identity = vec![Identity { category: "conference".into(), name: Some(room_id.into_inner().to_string()), r#type: "text".into(), }]; feature = vec![ Feature::new("http://jabber.org/protocol/disco#info"), Feature::new("http://jabber.org/protocol/disco#items"), Feature::new("http://jabber.org/protocol/muc"), ] } _ => { identity = vec![]; feature = vec![]; } }; Ok(InfoQuery { node: None, identity, feature, }) } async fn disco_items(&self, to: Option<&Jid>, req: &ItemQuery, core: &LavinaCore) -> ItemQuery { let item = match to { Some(Jid { name: None, server, resource: None, }) if server.0 == self.hostname => { vec![Item { jid: Jid { name: None, server: Server(self.hostname_rooms.clone()), resource: None, }, name: None, node: None, }] } Some(Jid { name: None, server, resource: None, }) if server.0 == self.hostname_rooms => { let room_list = core.get_all_rooms().await; room_list .into_iter() .map(|room_info| Item { jid: Jid { name: Some(Name(room_info.id.into_inner())), server: Server(self.hostname_rooms.clone()), resource: None, }, name: None, node: None, }) .collect() } _ => vec![], }; ItemQuery { item } } }