summaryrefslogblamecommitdiffstats
path: root/src/stanza/stream.rs
blob: 32f449d50d428fc42cb4c22353e31150d804a6b0 (plain) (tree)
1
2
3
4
5
6
7
8
9
             
 



                                
 

                                      































                                                              
                       

                            
                         
              

 







                                                                                               
                              



















                                                                            



                                                                                   











                                                             

 






































                                                                                               
                   







                                        
                        
             
                      
         
            
 




































                                                                                                  
use std::str;

use quick_xml::{
    events::{BytesStart, Event},
    name::QName,
};

use super::Element;
use crate::{JabberError, Result, JID};

const XMLNS_STREAM: &str = "http://etherx.jabber.org/streams";
const VERSION: &str = "1.0";

enum XMLNS {
    Client,
    Server,
}

impl From<XMLNS> for &str {
    fn from(xmlns: XMLNS) -> Self {
        match xmlns {
            XMLNS::Client => return "jabber:client",
            XMLNS::Server => return "jabber:server",
        }
    }
}

impl TryInto<XMLNS> for &str {
    type Error = JabberError;

    fn try_into(self) -> Result<XMLNS> {
        match self {
            "jabber:client" => Ok(XMLNS::Client),
            "jabber:server" => Ok(XMLNS::Server),
            _ => Err(JabberError::UnknownNamespace),
        }
    }
}

pub struct Stream {
    from: Option<JID>,
    id: Option<String>,
    to: Option<JID>,
    version: Option<String>,
    lang: Option<String>,
    ns: XMLNS,
}

impl Stream {
    pub fn new_client(from: &JID, to: &JID, id: Option<String>, lang: Option<String>) -> Self {
        Self {
            from: Some(from.clone()),
            id,
            to: Some(to.clone()),
            version: Some(VERSION.to_owned()),
            lang,
            ns: XMLNS::Client,
        }
    }

    fn build(&self) -> BytesStart {
        let mut start = BytesStart::new("stream:stream");
        if let Some(from) = &self.from {
            start.push_attribute(("from", from.to_string().as_str()));
        }
        if let Some(id) = &self.id {
            start.push_attribute(("id", id.as_str()));
        }
        if let Some(to) = &self.to {
            start.push_attribute(("to", to.to_string().as_str()));
        }
        if let Some(version) = &self.version {
            start.push_attribute(("version", version.to_string().as_str()));
        }
        if let Some(lang) = &self.lang {
            start.push_attribute(("xml:lang", lang.as_str()));
        }
        match &self.ns {
            XMLNS::Client => start.push_attribute(("xmlns", XMLNS::Client.into())),
            XMLNS::Server => start.push_attribute(("xmlns", XMLNS::Server.into())),
        }
        start.push_attribute(("xmlns:stream", XMLNS_STREAM));
        start
    }
}

impl<'e> Into<Element<'e>> for Stream {
    fn into(self) -> Element<'e> {
        Element {
            event: Event::Start(self.build().to_owned()),
            content: None,
        }
    }
}

impl<'e> TryFrom<Element<'e>> for Stream {
    type Error = JabberError;

    fn try_from(value: Element<'e>) -> Result<Stream> {
        let (mut from, mut id, mut to, mut version, mut lang, mut ns) =
            (None, None, None, None, None, XMLNS::Client);
        if let Event::Start(e) = value.event.as_ref() {
            for attribute in e.attributes() {
                let attribute = attribute?;
                match attribute.key {
                    QName(b"from") => {
                        from = Some(str::from_utf8(&attribute.value)?.to_string().try_into()?);
                    }
                    QName(b"id") => {
                        id = Some(str::from_utf8(&attribute.value)?.to_owned());
                    }
                    QName(b"to") => {
                        to = Some(str::from_utf8(&attribute.value)?.to_string().try_into()?);
                    }
                    QName(b"version") => {
                        version = Some(str::from_utf8(&attribute.value)?.to_owned());
                    }
                    QName(b"lang") => {
                        lang = Some(str::from_utf8(&attribute.value)?.to_owned());
                    }
                    QName(b"xmlns") => {
                        ns = str::from_utf8(&attribute.value)?.try_into()?;
                    }
                    _ => {
                        println!("unknown attribute: {:?}", attribute)
                    }
                }
            }
            Ok(Stream {
                from,
                id,
                to,
                version,
                lang,
                ns,
            })
        } else {
            Err(JabberError::ParseError)
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum StreamFeature {
    StartTls,
    Sasl(Vec<String>),
    Bind,
    Unknown,
}

impl<'e> TryFrom<Element<'e>> for Vec<StreamFeature> {
    type Error = JabberError;

    fn try_from(features_element: Element) -> Result<Self> {
        let mut features = Vec::new();
        if let Some(content) = features_element.content {
            for feature_element in content {
                match feature_element.event {
                    Event::Start(e) => match e.name() {
                        QName(b"starttls") => features.push(StreamFeature::StartTls),
                        QName(b"mechanisms") => {
                            let mut mechanisms = Vec::new();
                            if let Some(content) = feature_element.content {
                                for mechanism_element in content {
                                    if let Some(content) = mechanism_element.content {
                                        for mechanism_text in content {
                                            match mechanism_text.event {
                                                Event::Text(e) => mechanisms
                                                    .push(str::from_utf8(e.as_ref())?.to_owned()),
                                                _ => {}
                                            }
                                        }
                                    }
                                }
                            }
                            features.push(StreamFeature::Sasl(mechanisms))
                        }
                        _ => {}
                    },
                    _ => features.push(StreamFeature::Unknown),
                }
            }
        }
        Ok(features)
    }
}