summaryrefslogtreecommitdiffstats
path: root/src/jabber.rs
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@blos.sm>2023-10-21 01:28:54 +0100
committerLibravatar cel 🌸 <cel@blos.sm>2023-10-21 01:28:54 +0100
commite893869df974ebb7afcc318119840c53f8f377cb (patch)
tree3319dba477784c126011acc5422c9973782f7850 /src/jabber.rs
parentba94ee66fafbabd63d6d1ed5edf435d4c46c6796 (diff)
downloadluz-e893869df974ebb7afcc318119840c53f8f377cb.tar.gz
luz-e893869df974ebb7afcc318119840c53f8f377cb.tar.bz2
luz-e893869df974ebb7afcc318119840c53f8f377cb.zip
implement connection
Diffstat (limited to '')
-rw-r--r--src/jabber.rs296
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)
+ // }
+}