forked from lavina/lavina
				
			Compare commits
	
		
			3 Commits
		
	
	
		
			89918d9de1
			...
			dd33b41beb
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						dd33b41beb | |
| 
							
							
								
								 | 
						3724465c72 | |
| 
							
							
								
									
								
								 | 
						1b59250042 | 
| 
						 | 
				
			
			@ -20,7 +20,8 @@ impl<'a> XmppConnection<'a> {
 | 
			
		|||
        }) = m.to
 | 
			
		||||
        {
 | 
			
		||||
            if server.0.as_ref() == &*self.hostname_rooms && m.r#type == MessageType::Groupchat {
 | 
			
		||||
                self.user_handle.send_message(RoomId::from(name.0.clone())?, m.body.clone().into()).await?;
 | 
			
		||||
                let Some(body) = &m.body else { return Ok(()) };
 | 
			
		||||
                self.user_handle.send_message(RoomId::from(name.0.clone())?, body.clone()).await?;
 | 
			
		||||
                Message::<()> {
 | 
			
		||||
                    to: Some(Jid {
 | 
			
		||||
                        name: Some(self.user.xmpp_name.clone()),
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +43,8 @@ impl<'a> XmppConnection<'a> {
 | 
			
		|||
                .serialize(output);
 | 
			
		||||
                Ok(())
 | 
			
		||||
            } else if server.0.as_ref() == &*self.hostname && m.r#type == MessageType::Chat {
 | 
			
		||||
                self.user_handle.send_dialog_message(PlayerId::from(name.0.clone())?, m.body.clone()).await?;
 | 
			
		||||
                let Some(body) = &m.body else { return Ok(()) };
 | 
			
		||||
                self.user_handle.send_dialog_message(PlayerId::from(name.0.clone())?, body.clone()).await?;
 | 
			
		||||
                Ok(())
 | 
			
		||||
            } else {
 | 
			
		||||
                todo!()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,8 @@ use quick_xml::events::Event;
 | 
			
		|||
 | 
			
		||||
use lavina_core::room::RoomId;
 | 
			
		||||
use proto_xmpp::bind::{Jid, Name, Server};
 | 
			
		||||
use proto_xmpp::client::Presence;
 | 
			
		||||
use proto_xmpp::client::{Message, MessageType, Presence, Subject};
 | 
			
		||||
use proto_xmpp::muc::{Affiliation, Role, XUser, XUserItem};
 | 
			
		||||
use proto_xmpp::xml::{Ignore, ToXml};
 | 
			
		||||
 | 
			
		||||
use crate::XmppConnection;
 | 
			
		||||
| 
						 | 
				
			
			@ -22,8 +23,28 @@ impl<'a> XmppConnection<'a> {
 | 
			
		|||
                // resources in MUCs are members' personas – not implemented (yet?)
 | 
			
		||||
                resource: Some(_),
 | 
			
		||||
            }) if server.0 == self.hostname_rooms => {
 | 
			
		||||
                let response = self.muc_presence(&name).await?;
 | 
			
		||||
                let mut response = self.muc_presence(&name).await?;
 | 
			
		||||
                response.id = p.id;
 | 
			
		||||
                let subject = Message::<()> {
 | 
			
		||||
                    from: Some(Jid {
 | 
			
		||||
                        name: Some(name),
 | 
			
		||||
                        server: Server(self.hostname_rooms.clone()),
 | 
			
		||||
                        resource: None,
 | 
			
		||||
                    }),
 | 
			
		||||
                    id: None,
 | 
			
		||||
                    to: Some(Jid {
 | 
			
		||||
                        name: Some(self.user.xmpp_name.clone()),
 | 
			
		||||
                        server: Server(self.hostname.clone()),
 | 
			
		||||
                        resource: Some(self.user.xmpp_resource.clone()),
 | 
			
		||||
                    }),
 | 
			
		||||
                    r#type: MessageType::Groupchat,
 | 
			
		||||
                    lang: None,
 | 
			
		||||
                    subject: Some(Subject(None)),
 | 
			
		||||
                    body: None,
 | 
			
		||||
                    custom: vec![],
 | 
			
		||||
                };
 | 
			
		||||
                response.serialize(output);
 | 
			
		||||
                subject.serialize(output);
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                // TODO other presence cases
 | 
			
		||||
| 
						 | 
				
			
			@ -55,15 +76,17 @@ impl<'a> XmppConnection<'a> {
 | 
			
		|||
                };
 | 
			
		||||
                response.serialize(output);
 | 
			
		||||
            }
 | 
			
		||||
            _ => todo!(),
 | 
			
		||||
            e => {
 | 
			
		||||
                tracing::error!("TODO: unknown presence type: {e:?}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // todo: return Presence and serialize on the outside.
 | 
			
		||||
    async fn muc_presence(&mut self, name: &Name) -> Result<(Presence<()>)> {
 | 
			
		||||
    async fn muc_presence(&mut self, name: &Name) -> Result<(Presence<XUser>)> {
 | 
			
		||||
        let a = self.user_handle.join_room(RoomId::from(name.0.clone())?).await?;
 | 
			
		||||
        // TODO handle bans
 | 
			
		||||
        let response = Presence::<()> {
 | 
			
		||||
        let response = Presence {
 | 
			
		||||
            to: Some(Jid {
 | 
			
		||||
                name: Some(self.user.xmpp_name.clone()),
 | 
			
		||||
                server: Server(self.hostname.clone()),
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +97,17 @@ impl<'a> XmppConnection<'a> {
 | 
			
		|||
                server: Server(self.hostname_rooms.clone()),
 | 
			
		||||
                resource: Some(self.user.xmpp_muc_name.clone()),
 | 
			
		||||
            }),
 | 
			
		||||
            custom: vec![XUser {
 | 
			
		||||
                item: XUserItem {
 | 
			
		||||
                    affiliation: Affiliation::Member,
 | 
			
		||||
                    role: Role::Participant,
 | 
			
		||||
                    jid: Jid {
 | 
			
		||||
                        name: Some(self.user.xmpp_name.clone()),
 | 
			
		||||
                        server: Server(self.hostname.clone()),
 | 
			
		||||
                        resource: Some(self.user.xmpp_resource.clone()),
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
            }],
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        Ok(response)
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +125,7 @@ mod tests {
 | 
			
		|||
    use lavina_core::player::PlayerId;
 | 
			
		||||
    use proto_xmpp::bind::{Jid, Name, Resource, Server};
 | 
			
		||||
    use proto_xmpp::client::Presence;
 | 
			
		||||
    use proto_xmpp::muc::XUser;
 | 
			
		||||
 | 
			
		||||
    #[tokio::test]
 | 
			
		||||
    async fn test_muc_joining() -> Result<()> {
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +145,7 @@ mod tests {
 | 
			
		|||
        let mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        let response = conn.muc_presence(&user.xmpp_name).await.unwrap();
 | 
			
		||||
        let expected = Presence::<()> {
 | 
			
		||||
        let expected = Presence {
 | 
			
		||||
            to: Some(Jid {
 | 
			
		||||
                name: Some(conn.user.xmpp_name.clone()),
 | 
			
		||||
                server: Server(conn.hostname.clone()),
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +156,7 @@ mod tests {
 | 
			
		|||
                server: Server(conn.hostname_rooms.clone()),
 | 
			
		||||
                resource: Some(conn.user.xmpp_muc_name.clone()),
 | 
			
		||||
            }),
 | 
			
		||||
            custom: vec![XUser],
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        assert_eq!(expected, response);
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +185,7 @@ mod tests {
 | 
			
		|||
        let mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        let response = conn.muc_presence(&user.xmpp_name).await.unwrap();
 | 
			
		||||
        let expected = Presence::<()> {
 | 
			
		||||
        let expected = Presence {
 | 
			
		||||
            to: Some(Jid {
 | 
			
		||||
                name: Some(conn.user.xmpp_name.clone()),
 | 
			
		||||
                server: Server(conn.hostname.clone()),
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +196,7 @@ mod tests {
 | 
			
		|||
                server: Server(conn.hostname_rooms.clone()),
 | 
			
		||||
                resource: Some(conn.user.xmpp_muc_name.clone()),
 | 
			
		||||
            }),
 | 
			
		||||
            custom: vec![XUser],
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        assert_eq!(expected, response);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,8 +20,8 @@ pub struct Message<T> {
 | 
			
		|||
    // default is Normal
 | 
			
		||||
    pub r#type: MessageType,
 | 
			
		||||
    pub lang: Option<Str>,
 | 
			
		||||
    pub subject: Option<Str>,
 | 
			
		||||
    pub body: Str,
 | 
			
		||||
    pub subject: Option<Subject>,
 | 
			
		||||
    pub body: Option<Str>,
 | 
			
		||||
    pub custom: Vec<T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +38,20 @@ impl<T: FromXml> FromXml for Message<T> {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Eq, Debug)]
 | 
			
		||||
pub struct Subject(pub Option<Str>);
 | 
			
		||||
impl ToXml for Subject {
 | 
			
		||||
    fn serialize(&self, events: &mut Vec<Event<'static>>) {
 | 
			
		||||
        if let Some(ref s) = self.0 {
 | 
			
		||||
            events.push(Event::Start(BytesStart::new("subject")));
 | 
			
		||||
            events.push(Event::Text(BytesText::new(s).into_owned()));
 | 
			
		||||
            events.push(Event::End(BytesEnd::new("subject")));
 | 
			
		||||
        } else {
 | 
			
		||||
            events.push(Event::Empty(BytesStart::new("subject")));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(From)]
 | 
			
		||||
struct MessageParser<T: FromXml>(MessageParserInner<T>);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +71,7 @@ struct MessageParserState<T> {
 | 
			
		|||
    to: Option<Jid>,
 | 
			
		||||
    r#type: MessageType,
 | 
			
		||||
    lang: Option<Str>,
 | 
			
		||||
    subject: Option<Str>,
 | 
			
		||||
    subject: Option<Subject>,
 | 
			
		||||
    body: Option<Str>,
 | 
			
		||||
    custom: Vec<T>,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +144,7 @@ impl<T: FromXml> Parser for MessageParser<T> {
 | 
			
		|||
                            r#type: state.r#type,
 | 
			
		||||
                            lang: state.lang,
 | 
			
		||||
                            subject: state.subject,
 | 
			
		||||
                            body,
 | 
			
		||||
                            body: Some(body),
 | 
			
		||||
                            custom: state.custom,
 | 
			
		||||
                        }))
 | 
			
		||||
                    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +167,7 @@ impl<T: FromXml> Parser for MessageParser<T> {
 | 
			
		|||
            InSubject(mut state) => match event {
 | 
			
		||||
                Event::Text(ref bytes) => {
 | 
			
		||||
                    let subject = fail_fast!(std::str::from_utf8(&*bytes));
 | 
			
		||||
                    state.subject = Some(subject.into());
 | 
			
		||||
                    state.subject = Some(Subject(Some(subject.into())));
 | 
			
		||||
                    Continuation::Continue(InSubject(state).into())
 | 
			
		||||
                }
 | 
			
		||||
                Event::End(_) => Continuation::Continue(Outer(state).into()),
 | 
			
		||||
| 
						 | 
				
			
			@ -208,9 +222,14 @@ impl<T: ToXml> ToXml for Message<T> {
 | 
			
		|||
            value: self.r#type.as_str().as_bytes().into(),
 | 
			
		||||
        });
 | 
			
		||||
        events.push(Event::Start(bytes));
 | 
			
		||||
        if let Some(subject) = &self.subject {
 | 
			
		||||
            subject.serialize(events);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(body) = &self.body {
 | 
			
		||||
            events.push(Event::Start(BytesStart::new("body")));
 | 
			
		||||
        events.push(Event::Text(BytesText::new(&self.body).into_owned()));
 | 
			
		||||
            events.push(Event::Text(BytesText::new(body).into_owned()));
 | 
			
		||||
            events.push(Event::End(BytesEnd::new("body")));
 | 
			
		||||
        }
 | 
			
		||||
        events.push(Event::End(BytesEnd::new("message")));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -487,6 +506,7 @@ impl<T: ToXml> ToXml for Iq<T> {
 | 
			
		|||
 | 
			
		||||
#[derive(PartialEq, Eq, Debug)]
 | 
			
		||||
pub struct Presence<T> {
 | 
			
		||||
    pub id: Option<String>,
 | 
			
		||||
    pub to: Option<Jid>,
 | 
			
		||||
    pub from: Option<Jid>,
 | 
			
		||||
    pub priority: Option<PresencePriority>,
 | 
			
		||||
| 
						 | 
				
			
			@ -499,6 +519,7 @@ pub struct Presence<T> {
 | 
			
		|||
impl<T> Default for Presence<T> {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            id: Default::default(),
 | 
			
		||||
            to: Default::default(),
 | 
			
		||||
            from: Default::default(),
 | 
			
		||||
            priority: Default::default(),
 | 
			
		||||
| 
						 | 
				
			
			@ -573,6 +594,10 @@ impl<T: FromXml> FromXml for Presence<T> {
 | 
			
		|||
                        let s = std::str::from_utf8(&attr.value)?;
 | 
			
		||||
                        p.r#type = Some(s.into());
 | 
			
		||||
                    }
 | 
			
		||||
                    b"id" => {
 | 
			
		||||
                        let s = std::str::from_utf8(&attr.value)?;
 | 
			
		||||
                        p.id = Option::from(s.to_string());
 | 
			
		||||
                    }
 | 
			
		||||
                    _ => {}
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -660,6 +685,12 @@ impl<T: ToXml> ToXml for Presence<T> {
 | 
			
		|||
                value: from.to_string().as_bytes().into(),
 | 
			
		||||
            }]);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(ref id) = self.id {
 | 
			
		||||
            start.extend_attributes([Attribute {
 | 
			
		||||
                key: QName(b"id"),
 | 
			
		||||
                value: id.to_string().as_bytes().into(),
 | 
			
		||||
            }]);
 | 
			
		||||
        }
 | 
			
		||||
        events.push(Event::Start(start));
 | 
			
		||||
        if let Some(ref priority) = self.priority {
 | 
			
		||||
            let s = priority.0.to_string();
 | 
			
		||||
| 
						 | 
				
			
			@ -669,6 +700,9 @@ impl<T: ToXml> ToXml for Presence<T> {
 | 
			
		|||
                Event::End(BytesEnd::new("priority")),
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
        for c in &self.custom {
 | 
			
		||||
            c.serialize(events);
 | 
			
		||||
        }
 | 
			
		||||
        events.push(Event::End(BytesEnd::new("presence")));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,14 @@
 | 
			
		|||
#![allow(unused_variables)]
 | 
			
		||||
 | 
			
		||||
use quick_xml::events::Event;
 | 
			
		||||
use quick_xml::events::{BytesEnd, BytesStart, Event};
 | 
			
		||||
use quick_xml::name::ResolveResult;
 | 
			
		||||
 | 
			
		||||
use crate::bind::Jid;
 | 
			
		||||
use crate::xml::*;
 | 
			
		||||
use anyhow::{anyhow, Result};
 | 
			
		||||
 | 
			
		||||
pub const XMLNS: &'static str = "http://jabber.org/protocol/muc";
 | 
			
		||||
pub const XMLNS_USER: &'static str = "http://jabber.org/protocol/muc#user";
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Eq, Debug, Default)]
 | 
			
		||||
pub struct History {
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +145,95 @@ impl FromXml for X {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
pub struct XUser {
 | 
			
		||||
    pub item: XUserItem,
 | 
			
		||||
}
 | 
			
		||||
impl ToXml for XUser {
 | 
			
		||||
    fn serialize(&self, output: &mut Vec<Event<'static>>) {
 | 
			
		||||
        let mut tag = BytesStart::new("x");
 | 
			
		||||
        tag.push_attribute(("xmlns", XMLNS_USER));
 | 
			
		||||
        output.push(Event::Start(tag));
 | 
			
		||||
        self.item.serialize(output);
 | 
			
		||||
        output.push(Event::End(BytesEnd::new("x")));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
pub struct XUserItem {
 | 
			
		||||
    pub affiliation: Affiliation,
 | 
			
		||||
    pub jid: Jid,
 | 
			
		||||
    pub role: Role,
 | 
			
		||||
}
 | 
			
		||||
impl ToXml for XUserItem {
 | 
			
		||||
    fn serialize(&self, output: &mut Vec<Event<'static>>) {
 | 
			
		||||
        let mut meg = BytesStart::new("item");
 | 
			
		||||
        meg.push_attribute(("affiliation", self.affiliation.to_str()));
 | 
			
		||||
        meg.push_attribute(("role", self.role.to_str()));
 | 
			
		||||
        meg.push_attribute(("jid", self.jid.to_string().as_str()));
 | 
			
		||||
        output.push(Event::Empty(meg));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
pub enum Affiliation {
 | 
			
		||||
    Owner,
 | 
			
		||||
    Admin,
 | 
			
		||||
    Member,
 | 
			
		||||
    Outcast,
 | 
			
		||||
    None,
 | 
			
		||||
}
 | 
			
		||||
impl Affiliation {
 | 
			
		||||
    pub fn from_str(s: &str) -> Option<Self> {
 | 
			
		||||
        match s {
 | 
			
		||||
            "owner" => Some(Self::Owner),
 | 
			
		||||
            "admin" => Some(Self::Admin),
 | 
			
		||||
            "member" => Some(Self::Member),
 | 
			
		||||
            "outcast" => Some(Self::Outcast),
 | 
			
		||||
            "none" => Some(Self::None),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_str(&self) -> &str {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Owner => "owner",
 | 
			
		||||
            Self::Admin => "admin",
 | 
			
		||||
            Self::Member => "member",
 | 
			
		||||
            Self::Outcast => "outcast",
 | 
			
		||||
            Self::None => "none",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
pub enum Role {
 | 
			
		||||
    Moderator,
 | 
			
		||||
    Participant,
 | 
			
		||||
    Visitor,
 | 
			
		||||
    None,
 | 
			
		||||
}
 | 
			
		||||
impl Role {
 | 
			
		||||
    pub fn from_str(s: &str) -> Option<Self> {
 | 
			
		||||
        match s {
 | 
			
		||||
            "moderator" => Some(Self::Moderator),
 | 
			
		||||
            "participant" => Some(Self::Participant),
 | 
			
		||||
            "visitor" => Some(Self::Visitor),
 | 
			
		||||
            "none" => Some(Self::None),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_str(&self) -> &str {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Moderator => "moderator",
 | 
			
		||||
            Self::Participant => "participant",
 | 
			
		||||
            Self::Visitor => "visitor",
 | 
			
		||||
            Self::None => "none",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::*;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue