diff options
Diffstat (limited to '')
-rw-r--r-- | src/jabber.rs | 296 |
1 files changed, 180 insertions, 116 deletions
diff --git a/src/jabber.rs b/src/jabber.rs index d48eb9c..3583d19 100644 --- a/src/jabber.rs +++ b/src/jabber.rs @@ -1,141 +1,205 @@ -use std::marker::PhantomData; -use std::net::{IpAddr, SocketAddr}; -use std::str::FromStr; use std::sync::Arc; -use quick_xml::{NsReader, Writer}; +use quick_xml::{events::Event, se::Serializer, NsReader, Writer}; use rsasl::prelude::SASLConfig; -use tokio::io::BufReader; -use tokio::net::TcpStream; -use tokio_native_tls::native_tls::TlsConnector; +use serde::Serialize; +use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, ReadHalf, WriteHalf}; -use crate::client::JabberClientType; -use crate::jid::JID; -use crate::{client, JabberClient}; -use crate::{JabberError, Result}; +use crate::connection::{Tls, Unencrypted}; +use crate::error::JabberError; +use crate::stanza::stream::Stream; +use crate::stanza::DECLARATION; +use crate::Result; +use crate::JID; -pub struct Jabber<'j> { - pub jid: JID, - pub auth: Arc<SASLConfig>, - pub server: String, - _marker: PhantomData<&'j ()>, +pub struct Jabber<S> +where + S: AsyncRead + AsyncWrite + Unpin, +{ + reader: NsReader<BufReader<ReadHalf<S>>>, + writer: Writer<WriteHalf<S>>, + jid: Option<JID>, + auth: Option<Arc<SASLConfig>>, + server: String, } -impl<'j> Jabber<'j> { - pub fn user(jid: JID, password: String) -> Result<Self> { - let server = jid.domainpart.clone(); - let auth = SASLConfig::with_credentials(None, jid.localpart.clone().unwrap(), password)?; - println!("auth: {:?}", auth); - Ok(Self { +impl<S> Jabber<S> +where + S: AsyncRead + AsyncWrite + Unpin, +{ + pub fn new( + reader: ReadHalf<S>, + writer: WriteHalf<S>, + jid: Option<JID>, + auth: Option<Arc<SASLConfig>>, + server: String, + ) -> Self { + let reader = NsReader::from_reader(BufReader::new(reader)); + let writer = Writer::new(writer); + Self { + reader, + writer, jid, auth, server, - _marker: PhantomData, - }) - } - - pub async fn login(&'j mut self) -> Result<JabberClient<'j>> { - let mut client = self.connect().await?.ensure_tls().await?; - client.start_stream().await?; - client.negotiate().await?; - Ok(client) + } } +} - async fn get_sockets(&self) -> Vec<(SocketAddr, bool)> { - let mut socket_addrs = Vec::new(); +impl<S> Jabber<S> +where + S: AsyncRead + AsyncWrite + Unpin, + Writer<tokio::io::WriteHalf<S>>: AsyncWriteExt, + Writer<tokio::io::WriteHalf<S>>: AsyncWrite, +{ + pub async fn start_stream(&mut self) -> Result<()> { + // client to server - // if it's a socket/ip then just return that + // declaration + self.writer.write_event_async(DECLARATION.clone()).await?; - // socket - if let Ok(socket_addr) = SocketAddr::from_str(&self.jid.domainpart) { - match socket_addr.port() { - 5223 => socket_addrs.push((socket_addr, true)), - _ => socket_addrs.push((socket_addr, false)), - } + // opening stream element + let server = &self.server.to_owned().try_into()?; + let stream_element = Stream::new_client(None, server, None, "en"); + // TODO: nicer function to serialize to xml writer + let mut buffer = String::new(); + let ser = Serializer::new(&mut buffer); + stream_element.serialize(ser).unwrap(); + self.writer.write_all(buffer.as_bytes()); - return socket_addrs; - } - // ip - if let Ok(ip) = IpAddr::from_str(&self.jid.domainpart) { - socket_addrs.push((SocketAddr::new(ip, 5222), false)); - socket_addrs.push((SocketAddr::new(ip, 5223), true)); - return socket_addrs; - } + // server to client - // otherwise resolve - if let Ok(resolver) = trust_dns_resolver::AsyncResolver::tokio_from_system_conf() { - if let Ok(lookup) = resolver - .srv_lookup(format!("_xmpp-client._tcp.{}", self.jid.domainpart)) - .await - { - for srv in lookup { - resolver - .lookup_ip(srv.target().to_owned()) - .await - .map(|ips| { - for ip in ips { - socket_addrs.push((SocketAddr::new(ip, srv.port()), false)) - } - }); - } - } - if let Ok(lookup) = resolver - .srv_lookup(format!("_xmpps-client._tcp.{}", self.jid.domainpart)) - .await - { - for srv in lookup { - resolver - .lookup_ip(srv.target().to_owned()) - .await - .map(|ips| { - for ip in ips { - socket_addrs.push((SocketAddr::new(ip, srv.port()), true)) - } - }); + // may or may not send a declaration + let mut buf = Vec::new(); + let mut first_event = self.reader.read_resolved_event_into_async(&mut buf).await?; + match first_event { + (quick_xml::name::ResolveResult::Unbound, Event::Decl(e)) => { + if let Ok(version) = e.version() { + if version.as_ref() == b"1.0" { + first_event = self.reader.read_resolved_event_into_async(&mut buf).await? + } else { + // todo: error + todo!() + } + } else { + first_event = self.reader.read_resolved_event_into_async(&mut buf).await? } } - - // in case cannot connect through SRV records - resolver.lookup_ip(&self.jid.domainpart).await.map(|ips| { - for ip in ips { - socket_addrs.push((SocketAddr::new(ip, 5222), false)); - socket_addrs.push((SocketAddr::new(ip, 5223), true)); - } - }); + _ => (), } - socket_addrs - } - /// establishes a connection to the server - pub async fn connect(&'j mut self) -> Result<JabberClientType> { - for (socket_addr, is_tls) in self.get_sockets().await { - println!("trying {}", socket_addr); - match is_tls { - true => { - let socket = TcpStream::connect(socket_addr).await.unwrap(); - let connector = TlsConnector::new().unwrap(); - if let Ok(stream) = tokio_native_tls::TlsConnector::from(connector) - .connect(&self.server, socket) - .await - { - let (read, write) = tokio::io::split(stream); - let reader = NsReader::from_reader(BufReader::new(read)); - let writer = Writer::new(write); - let client = client::encrypted::JabberClient::new(reader, writer, self); - return Ok(JabberClientType::Encrypted(client)); - } - } - false => { - if let Ok(stream) = TcpStream::connect(socket_addr).await { - let (read, write) = tokio::io::split(stream); - let reader = NsReader::from_reader(BufReader::new(read)); - let writer = Writer::new(write); - let client = client::unencrypted::JabberClient::new(reader, writer, self); - return Ok(JabberClientType::Unencrypted(client)); - } + // receive stream element and validate + let stream_response: Stream; + match first_event { + (quick_xml::name::ResolveResult::Bound(ns), Event::Start(e)) => { + if ns.0 == crate::stanza::stream::XMLNS.as_bytes() { + // stream_response = Stream::new( + // e.try_get_attribute("from")?.try_map(|attribute| { + // str::from_utf8(attribute.value.as_ref())? + // .try_into()? + // .as_ref() + // })?, + // e.try_get_attribute("to")?.try_map(|attribute| { + // str::from_utf8(attribute.value.as_ref())? + // .try_into()? + // .as_ref() + // })?, + // e.try_get_attribute("id")?.try_map(|attribute| { + // str::from_utf8(attribute.value.as_ref())? + // .try_into()? + // .as_ref() + // })?, + // e.try_get_attribute("version")?.try_map(|attribute| { + // str::from_utf8(attribute.value.as_ref())? + // .try_into()? + // .as_ref() + // })?, + // e.try_get_attribute("lang")?.try_map(|attribute| { + // str::from_utf8(attribute.value.as_ref())? + // .try_into()? + // .as_ref() + // })?, + // ); + return Ok(()); + } else { + return Err(JabberError::BadStream); } } + // TODO: errors for incorrect namespace + (quick_xml::name::ResolveResult::Unbound, Event::Decl(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::Start(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::End(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::Empty(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::Text(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::CData(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::Comment(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::Decl(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::PI(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::DocType(_)) => todo!(), + (quick_xml::name::ResolveResult::Unknown(_), Event::Eof) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::Start(_)) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::End(_)) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::Empty(_)) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::Text(_)) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::CData(_)) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::Comment(_)) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::PI(_)) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::DocType(_)) => todo!(), + (quick_xml::name::ResolveResult::Unbound, Event::Eof) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::End(_)) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::Empty(_)) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::Text(_)) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::CData(_)) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::Comment(_)) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::Decl(_)) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::PI(_)) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::DocType(_)) => todo!(), + (quick_xml::name::ResolveResult::Bound(_), Event::Eof) => todo!(), } - Err(JabberError::Connection) } } + +// pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> { +// Element::read(&mut self.reader).await?.try_into() +// } + +impl Jabber<Unencrypted> { + pub async fn starttls(mut self) -> Result<Jabber<Tls>> { + todo!() + } + // let mut starttls_element = BytesStart::new("starttls"); + // starttls_element.push_attribute(("xmlns", "urn:ietf:params:xml:ns:xmpp-tls")); + // self.writer + // .write_event_async(Event::Empty(starttls_element)) + // .await + // .unwrap(); + // let mut buf = Vec::new(); + // match self.reader.read_event_into_async(&mut buf).await.unwrap() { + // Event::Empty(e) => match e.name() { + // QName(b"proceed") => { + // let connector = TlsConnector::new().unwrap(); + // let stream = self + // .reader + // .into_inner() + // .into_inner() + // .unsplit(self.writer.into_inner()); + // if let Ok(tlsstream) = tokio_native_tls::TlsConnector::from(connector) + // .connect(&self.jabber.server, stream) + // .await + // { + // let (read, write) = tokio::io::split(tlsstream); + // let reader = Reader::from_reader(BufReader::new(read)); + // let writer = Writer::new(write); + // let mut client = + // super::encrypted::JabberClient::new(reader, writer, self.jabber); + // client.start_stream().await?; + // return Ok(client); + // } + // } + // QName(_) => return Err(JabberError::TlsNegotiation), + // }, + // _ => return Err(JabberError::TlsNegotiation), + // } + // Err(JabberError::TlsNegotiation) + // } +} |