// TODO: logging (dropped errors)
#![allow(unused_must_use)]
use std::{
net::{IpAddr, SocketAddr},
str::FromStr,
};
use jid::JID;
use quick_xml::{Reader, Writer};
use tokio::net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
};
pub mod jid;
pub struct JabberData {
jid: jid::JID,
password: String,
}
impl JabberData {
pub fn new(jid: JID, password: String) -> Self {
Self { jid, password }
}
async fn get_sockets(&self) -> Vec<SocketAddr> {
let mut socket_addrs = Vec::new();
// if it's a socket/ip then just return that
// socket
if let Ok(socket_addr) = SocketAddr::from_str(&self.jid.domainpart) {
socket_addrs.push(socket_addr);
return socket_addrs;
}
// ip
if let Ok(ip) = IpAddr::from_str(&self.jid.domainpart) {
socket_addrs.push(SocketAddr::new(ip, 5222));
socket_addrs.push(SocketAddr::new(ip, 5223));
return socket_addrs;
}
// if port specified return name resolutions with specified port
// 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()))
}
});
}
}
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()))
}
});
}
}
// 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));
socket_addrs.push(SocketAddr::new(ip, 5223));
}
});
}
socket_addrs
}
}
pub struct Jabber {
reader: Reader<OwnedReadHalf>,
writer: Writer<OwnedWriteHalf>,
data: JabberData,
}
#[derive(Debug)]
pub enum JabberError {
NotConnected,
}
impl Jabber {
pub async fn connect(data: JabberData) -> Result<Self, JabberError> {
for socket_addr in data.get_sockets().await {
println!("trying {}", socket_addr);
if let Ok(stream) = TcpStream::connect(socket_addr).await {
println!("connected to {}", socket_addr);
let (read, write) = stream.into_split();
return Ok(Self {
reader: Reader::from_reader(read),
writer: Writer::new(write),
data,
});
}
}
Err(JabberError::NotConnected)
}
async fn reconnect(&mut self) {
for socket_addr in self.data.get_sockets().await {
println!("trying {}", socket_addr);
if let Ok(stream) = TcpStream::connect(socket_addr).await {
println!("connected to {}", socket_addr);
let (read, write) = stream.into_split();
self.reader = Reader::from_reader(read);
self.writer = Writer::new(write);
return;
}
}
println!("could not connect")
}
async fn begin_stream(&mut self) -> Result<(), JabberError> {
todo!()
}
async fn starttls() -> Result<(), JabberError> {
todo!()
}
async fn directtls() -> Result<(), JabberError> {
todo!()
}
async fn auth(&mut self) -> Result<(), JabberError> {
todo!()
}
async fn close(&mut self) {}
}
#[cfg(test)]
mod tests {
use crate::jid::JID;
use super::*;
#[tokio::test]
async fn get_sockets() {
let data = JabberData::new(JID::from_str("cel@blos.sm").unwrap(), "password".to_owned());
println!("{:?}", data.get_sockets().await)
}
#[tokio::test]
async fn connect() {
Jabber::connect(JabberData::new(
JID::from_str("cel@blos.sm").unwrap(),
"password".to_owned(),
))
.await
.unwrap();
}
}