summaryrefslogblamecommitdiffstats
path: root/src/lib.rs
blob: 10c71722b52286a7d331380ef53065fb64e7a01d (plain) (tree)
1
2
3
4
5
6
7






                                 





                                         
 
            
 

                       


                     


                                                    
     

                                                    




                                                    
                                                                             



                                           
                                                                









                                                                                           
                                                                                 













                                                                                  
                                                                                  














                                                                                  
                                                                      








                                                                 











                                   
 


                                                                         


                                                                       


















                                                                       




                                     

















                                                                 



            

                        

                 

                            











                                                                                                 

     
// 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();
    }
}