diff options
| author | 2025-03-28 22:44:11 +0000 | |
|---|---|---|
| committer | 2025-03-28 22:44:11 +0000 | |
| commit | ba5ad94525940e3e34983425961550c67afc49ae (patch) | |
| tree | ed0c93a75fac64d0d807ae77f222c501dd7da904 /filamento | |
| parent | 20df3b7d6f3213bc3f5a90cc36363865b1b9e966 (diff) | |
| download | luz-ba5ad94525940e3e34983425961550c67afc49ae.tar.gz luz-ba5ad94525940e3e34983425961550c67afc49ae.tar.bz2 luz-ba5ad94525940e3e34983425961550c67afc49ae.zip | |
feat(filamento): disco info requests
Diffstat (limited to '')
| -rw-r--r-- | filamento/examples/example.rs | 8 | ||||
| -rw-r--r-- | filamento/src/disco.rs | 43 | ||||
| -rw-r--r-- | filamento/src/error.rs | 25 | ||||
| -rw-r--r-- | filamento/src/lib.rs | 24 | ||||
| -rw-r--r-- | filamento/src/logic/offline.rs | 5 | ||||
| -rw-r--r-- | filamento/src/logic/online.rs | 82 | 
6 files changed, 175 insertions, 12 deletions
| diff --git a/filamento/examples/example.rs b/filamento/examples/example.rs index b1ab6ce..10267bc 100644 --- a/filamento/examples/example.rs +++ b/filamento/examples/example.rs @@ -20,7 +20,7 @@ async fn main() {      });      client.connect().await.unwrap(); -    tokio::time::sleep(Duration::from_secs(15)).await; +    tokio::time::sleep(Duration::from_secs(5)).await;      info!("sending message");      client          .send_message( @@ -31,5 +31,9 @@ async fn main() {          )          .await          .unwrap(); -    println!("sent message"); +    info!("sent message"); +    info!("sending disco query"); +    let info = client.disco_info(None).await.unwrap(); +    tokio::time::sleep(Duration::from_secs(5)).await; +    info!("got disco result: {:#?}", info);  } diff --git a/filamento/src/disco.rs b/filamento/src/disco.rs index 86e339e..ccc99cb 100644 --- a/filamento/src/disco.rs +++ b/filamento/src/disco.rs @@ -4,6 +4,7 @@ use stanza::xep_0030::{info, items};  pub use feature::Feature;  pub use identity::Identity; +#[derive(Debug, Clone)]  pub struct Info {      node: Option<String>,      features: Vec<Feature>, @@ -109,6 +110,7 @@ mod feature {      use stanza::xep_0030::info;      // https://xmpp.org/registrar/disco-features.html +    #[derive(Debug, Clone)]      pub enum Feature {          DNSSRV,          FullUnicode, @@ -527,6 +529,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum Amp {          Errors,          Action(AmpAction), @@ -543,6 +546,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum AmpAction {          Alert,          Drop, @@ -562,6 +566,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum AmpCondition {          Deliver,          ExpireAt, @@ -581,6 +586,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum Bytestreams {          UDP,      } @@ -594,6 +600,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum CapsVersion {          One(Option<CapsOne>),          Two(Option<CapsTwo>), @@ -614,6 +621,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum CapsOne {          Optimize,      } @@ -627,6 +635,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum CapsTwo {          Optimize,      } @@ -640,6 +649,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum Disco {          Info,          Items, @@ -655,6 +665,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum MUC {          Admin,          Owner, @@ -704,6 +715,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum PubSub {          AccessAuthorize,          AccessOpen, @@ -821,6 +833,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum SOAP {          Fault,      } @@ -834,6 +847,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum WaitingList {          Schemes(WaitingListSchemes),      } @@ -846,6 +860,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum WaitingListSchemes {          Mailto,          Tel, @@ -863,6 +878,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum Component {          Accept,          Connect, @@ -878,6 +894,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum Iq {          Auth,          Gateway, @@ -911,6 +928,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum X {          Data,          Encrypted, @@ -930,6 +948,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum XMPPSASL {          C2S,          S2S, @@ -945,6 +964,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum XMPPTLS {          C2S,          S2S, @@ -960,6 +980,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum Archive {          Auto,          Manage, @@ -979,6 +1000,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum Avatar {          Data,          Metadata, @@ -994,6 +1016,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum Jingle {          Apps(JingleApps),      } @@ -1006,6 +1029,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum JingleApps {          RTP(JingleAppsRTP),      } @@ -1018,6 +1042,7 @@ mod feature {          }      } +    #[derive(Debug, Clone)]      pub enum JingleAppsRTP {          Audio,          Video, @@ -1037,6 +1062,7 @@ mod feature {  mod identity {      use stanza::xep_0030::info; +    #[derive(Debug, Clone)]      pub struct Identity {          name: Option<String>,          category: Category, @@ -1065,6 +1091,7 @@ mod identity {      // TODO: separate crate for disco registry      /// categories taken from [XMPP Disco Categories](https://xmpp.org/registrar/disco-categories.html) +    #[derive(Debug, Clone)]      pub enum Category {          Account(Account),          Auth(Auth), @@ -1165,6 +1192,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Account {          Admin,          Anonymous, @@ -1195,6 +1223,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Auth {          Cert,          Generic, @@ -1234,6 +1263,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Authz {          Ephemeral,          Other(String), @@ -1258,6 +1288,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Automation {          CommandList,          CommandNode, @@ -1294,6 +1325,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Client {          Bot,          Console, @@ -1342,6 +1374,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Collaboration {          Whiteboard,          Other(String), @@ -1366,6 +1399,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Component {          Archive,          C2S, @@ -1417,6 +1451,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Conference {          IRC,          Text, @@ -1444,6 +1479,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Directory {          Chatroom,          Group, @@ -1477,6 +1513,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Gateway {          Aim,          Discord, @@ -1579,6 +1616,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Headline {          NewMail,          RSS, @@ -1609,6 +1647,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Hierarchy {          Branch,          Leaf, @@ -1636,6 +1675,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Proxy {          Bytestreams,          Other(String), @@ -1660,6 +1700,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum PubSub {          Collection,          Leaf, @@ -1694,6 +1735,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Server {          IM,          Other(String), @@ -1718,6 +1760,7 @@ mod identity {          }      } +    #[derive(Debug, Clone)]      pub enum Store {          Berkeley,          File, diff --git a/filamento/src/error.rs b/filamento/src/error.rs index ccb4406..8e2e4be 100644 --- a/filamento/src/error.rs +++ b/filamento/src/error.rs @@ -1,7 +1,8 @@  use std::sync::Arc; +use jid::JID;  use lampada::error::{ConnectionError, ReadError, WriteError}; -use stanza::client::Stanza; +use stanza::client::{Stanza, iq::Query};  use thiserror::Error;  pub use lampada::error::CommandError; @@ -80,8 +81,6 @@ pub enum RosterError {      // TODO: display for stanza, to show as xml, same for read error types.      #[error("unexpected reply: {0:?}")]      UnexpectedStanza(Stanza), -    #[error("stream read: {0}")] -    Read(#[from] ReadError),      #[error("stanza error: {0}")]      StanzaError(#[from] stanza::client::error::Error),      #[error("could not reply to roster push: {0}")] @@ -89,6 +88,26 @@ pub enum RosterError {  }  #[derive(Debug, Error, Clone)] +pub enum DiscoError { +    #[error("write error: {0}")] +    Write(#[from] WriteError), +    #[error("iq response: {0}")] +    IqResponse(#[from] RequestError), +    #[error("reply from incorrect entity: {0}")] +    IncorrectEntity(JID), +    #[error("unexpected reply: {0:?}")] +    UnexpectedStanza(Stanza), +    #[error("stanza error: {0}")] +    StanzaError(#[from] stanza::client::error::Error), +    #[error("disco result missing query item")] +    MissingQuery, +    #[error("disco error missing error")] +    MissingError, +    #[error("received mismatched query")] +    MismatchedQuery(Query), +} + +#[derive(Debug, Error, Clone)]  pub enum RequestError {      #[error("sending request: {0}")]      Write(#[from] WriteError), diff --git a/filamento/src/lib.rs b/filamento/src/lib.rs index bc946ae..ed33e99 100644 --- a/filamento/src/lib.rs +++ b/filamento/src/lib.rs @@ -9,8 +9,9 @@ use std::{  use chat::{Body, Chat, Message};  use chrono::Utc;  use db::Db; +use disco::Info;  use error::{ -    ConnectionJobError, DatabaseError, Error, IqError, MessageRecvError, PresenceError, +    ConnectionJobError, DatabaseError, DiscoError, Error, IqError, MessageRecvError, PresenceError,      RosterError, StatusError,  };  use futures::FutureExt; @@ -98,8 +99,13 @@ pub enum Command {      /// send a message to a jid (any kind of jid that can receive a message, e.g. a user or a      /// chatroom). if disconnected, will be cached so when client connects, message will be sent.      SendMessage(JID, Body, oneshot::Sender<Result<(), WriteError>>), -    /// disco info request -    DiscoInfo(JID, oneshot::Sender<Result>), +    /// disco info query +    DiscoInfo( +        Option<JID>, +        oneshot::Sender<Result<disco::Info, DiscoError>>, +    ), +    // /// disco items query +    // DiscoItems(JID, oneshot::Sender<Result<disco::Info, DiscoError>>),  }  #[derive(Debug, Clone)] @@ -473,6 +479,18 @@ impl Client {              .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))??;          Ok(result)      } + +    pub async fn disco_info(&self, jid: Option<JID>) -> Result<Info, CommandError<DiscoError>> { +        let (send, recv) = oneshot::channel(); +        self.send(CoreClientCommand::Command(Command::DiscoInfo(jid, send))) +            .await +            .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?; +        let result = timeout(self.timeout, recv) +            .await +            .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))? +            .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))??; +        Ok(result) +    }  }  impl From<Command> for CoreClientCommand<Command> { diff --git a/filamento/src/logic/offline.rs b/filamento/src/logic/offline.rs index e864f22..7dfb394 100644 --- a/filamento/src/logic/offline.rs +++ b/filamento/src/logic/offline.rs @@ -2,7 +2,7 @@ use lampada::error::WriteError;  use crate::{      Command, -    error::{DatabaseError, Error, RosterError, StatusError}, +    error::{DatabaseError, DiscoError, Error, RosterError, StatusError},      presence::Online,      roster::Contact,  }; @@ -113,6 +113,9 @@ pub async fn handle_offline_result(logic: &ClientLogic, command: Command) -> Res          Command::SendPresence(_jid, _presence, sender) => {              sender.send(Err(WriteError::Disconnected));          } +        Command::DiscoInfo(_jid, sender) => { +            sender.send(Err(DiscoError::Write(WriteError::Disconnected))); +        }      }      Ok(())  } diff --git a/filamento/src/logic/online.rs b/filamento/src/logic/online.rs index 05d3f2b..c76907b 100644 --- a/filamento/src/logic/online.rs +++ b/filamento/src/logic/online.rs @@ -4,18 +4,20 @@ use lampada::{Connected, WriteMessage, error::WriteError};  use stanza::{      client::{          Stanza, -        iq::{self, Iq, IqType}, +        iq::{self, Iq, IqType, Query},      }, +    xep_0030::info,      xep_0203::Delay,  };  use tokio::sync::oneshot; -use tracing::{debug, info}; +use tracing::{debug, error, info};  use uuid::Uuid;  use crate::{      Command, UpdateMessage,      chat::{Body, Message}, -    error::{DatabaseError, Error, MessageSendError, RosterError, StatusError}, +    disco::Info, +    error::{DatabaseError, DiscoError, Error, MessageSendError, RosterError, StatusError},      presence::{Online, Presence, PresenceType},      roster::{Contact, ContactUpdate},  }; @@ -532,6 +534,76 @@ pub async fn handle_send_presence(      Ok(())  } +// TODO: cache disco infos +pub async fn handle_disco_info( +    logic: &ClientLogic, +    connection: Connected, +    jid: Option<JID>, +) -> Result<Info, DiscoError> { +    let id = Uuid::new_v4().to_string(); +    let request = Iq { +        from: Some(connection.jid().clone()), +        id: id.clone(), +        to: jid.clone(), +        r#type: IqType::Get, +        lang: None, +        query: Some(Query::DiscoInfo(info::Query { +            node: None, +            features: Vec::new(), +            identities: Vec::new(), +        })), +        errors: Vec::new(), +    }; +    match logic +        .pending() +        .request(&connection, Stanza::Iq(request), id) +        .await? +    { +        Stanza::Iq(Iq { +            from, +            r#type, +            query, +            mut errors, +            .. +            // TODO: maybe abstract a bunch of these different errors related to iqs into an iq error thing? as in like call iq.result(), get the query from inside, error otherwise. +        }) if r#type == IqType::Result || r#type == IqType::Error => { +            if from == jid || { +                if jid == None { +                    from == Some(connection.jid().as_bare()) +                } else { +                    false +                } +            } { +                match r#type { +                    IqType::Result => { +                        if let Some(query) = query { +                            match query { +                                Query::DiscoInfo(info) => Ok(info.into()), +                                q => Err(DiscoError::MismatchedQuery(q)), +                            } +                        } else { +                            Err(DiscoError::MissingQuery) +                        } +                    } +                    IqType::Error => { +                        if let Some(error) = errors.pop() { +                            Err(error.into()) +                        } else { +                            Err(DiscoError::MissingError) +                        } +                    } +                    _ => unreachable!(), +                } +            } else { +                Err(DiscoError::IncorrectEntity( +                    from.unwrap_or_else(|| connection.jid().as_bare()), +                )) +            } +        } +        s => Err(DiscoError::UnexpectedStanza(s)), +    } +} +  // TODO: could probably macro-ise?  pub async fn handle_online_result(      logic: &ClientLogic, @@ -629,6 +701,10 @@ pub async fn handle_online_result(              let result = handle_send_presence(connection, jid, presence).await;              let _ = sender.send(result);          } +        Command::DiscoInfo(jid, sender) => { +            let result = handle_disco_info(logic, connection, jid).await; +            let _ = sender.send(result); +        }      }      Ok(())  } | 
