diff options
| author | 2025-02-12 17:33:12 +0000 | |
|---|---|---|
| committer | 2025-02-12 17:33:12 +0000 | |
| commit | 05b0d38490a69d058cdd0ee7b17140634d116af2 (patch) | |
| tree | 2a6c29f55fd7f6db16d67da000d2beedb5d72c8d | |
| parent | 8e6aa698b35f62dcd3d5c627f39dde53d0b1154d (diff) | |
| download | luz-05b0d38490a69d058cdd0ee7b17140634d116af2.tar.gz luz-05b0d38490a69d058cdd0ee7b17140634d116af2.tar.bz2 luz-05b0d38490a69d058cdd0ee7b17140634d116af2.zip | |
WIP: rfc 6121 data(base)types
Diffstat (limited to '')
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | luz/Cargo.toml | 1 | ||||
| -rw-r--r-- | luz/migrations/20240113011930_luz.sql | 54 | ||||
| -rw-r--r-- | luz/src/chat.rs | 30 | ||||
| -rw-r--r-- | luz/src/error.rs | 26 | ||||
| -rw-r--r-- | luz/src/lib.rs | 72 | ||||
| -rw-r--r-- | luz/src/presence.rs | 19 | ||||
| -rw-r--r-- | luz/src/roster.rs | 29 | 
8 files changed, 211 insertions, 21 deletions
| @@ -73,6 +73,7 @@ file/media sharing (further research needed):  - [ ] xep-0234: jingle file transfer  need more research: +- [ ] xep-0154: user profile  - [ ] message editing    - [ ] xep-0308: last message correction (should not be used for older than last message according to spec)  - [ ] chat read markers diff --git a/luz/Cargo.toml b/luz/Cargo.toml index ebff7d9..cd86ce2 100644 --- a/luz/Cargo.toml +++ b/luz/Cargo.toml @@ -15,3 +15,4 @@ tokio-stream = "0.1.17"  tokio-util = "0.7.13"  tracing = "0.1.41"  tracing-subscriber = "0.3.19" +uuid = "1.13.1" diff --git a/luz/migrations/20240113011930_luz.sql b/luz/migrations/20240113011930_luz.sql index bae43ea..a758a23 100644 --- a/luz/migrations/20240113011930_luz.sql +++ b/luz/migrations/20240113011930_luz.sql @@ -1,5 +1,53 @@ +PRAGMA foreign_keys = on; + +-- a user jid will never change, only a chat user will change +-- TODO: avatar, nick, etc. +create table user( +    jid jid primary key, +    cached_status text, +); + +-- enum for subscription state +create table subscription( +    state text primary key, +); + +insert into subscription ( state ) values ('none'), ('pending-out'), ('pending-in'), ('only-out'), ('only-in'), ('out-pending-in'), ('in-pending-out'), ('buddy'); + +-- a roster contains users, with client-set nickname  CREATE TABLE roster(  -    id INTEGER PRIMARY KEY, -    jid TEXT NOT NULL, -    nickname TEXT, +    jid jid primary key, +    name TEXT, +    subscription text not null, +    foreign key(subscription) references subscription(state), +    foreign key(jid) references users(jid) +); + +create table groups( +    group text primary key +); + +create table groups_roster( +    group_id text, +    contact_id jid, +    foreign key(group_id) references group(id), +    foreign key(contact_id) references roster(id), +    primary key(group_id, contact_id) +); + +-- chat includes reference to user jid chat is with +create table chats ( +    id uuid primary key, +    contact_id jid not null unique, +); + +-- messages include reference to chat they are in, and who sent them. +create table messages ( +    id uuid primary key, +    body text, +    chat_id uuid not null, +    from jid not null, +    -- TODO: read bool not null, +    foreign key(chat_id) references chats(id), +    foreign key(from) references users(jid)  ); diff --git a/luz/src/chat.rs b/luz/src/chat.rs new file mode 100644 index 0000000..a084d29 --- /dev/null +++ b/luz/src/chat.rs @@ -0,0 +1,30 @@ +use uuid::Uuid; + +use crate::roster::Contact; + +pub enum Chat { +    Direct(DM), +    Channel(Channel), +} + +#[derive(Debug)] +pub struct Message { +    id: Uuid, +    // contains full contact information +    from: Contact, +    // TODO: rich text, other contents, threads +    body: Body, +} + +#[derive(Debug)] +pub struct Body { +    body: String, +} + +pub struct DM { +    contact: Contact, +    message_history: Vec<Message>, +} + +// TODO: group chats +pub struct Channel {} diff --git a/luz/src/error.rs b/luz/src/error.rs index 6c3fb5d..16e1c6e 100644 --- a/luz/src/error.rs +++ b/luz/src/error.rs @@ -1,11 +1,23 @@  #[derive(Debug)]  pub enum Error {      AlreadyConnected, +    Presence(Reason), +    Roster(Reason), +    SendMessage(Reason), +    AlreadyDisconnected, +    LostConnection, +} + +#[derive(Debug)] +pub enum Reason { +    // TODO: organisastion of error into internal error thing +    Timeout, +    Stream(stanza::stream_error::Error), +    Stanza(stanza::stanza_error::Error),      Jabber(jabber::Error),      XML(peanuts::Error),      SQL(sqlx::Error), -    JID(jid::ParseError), -    AlreadyDisconnected, +    // JID(jid::ParseError),      LostConnection,  } @@ -15,11 +27,11 @@ impl From<peanuts::Error> for Error {      }  } -impl From<jid::ParseError> for Error { -    fn from(e: jid::ParseError) -> Self { -        Self::JID(e) -    } -} +// impl From<jid::ParseError> for Error { +//     fn from(e: jid::ParseError) -> Self { +//         Self::JID(e) +//     } +// }  impl From<sqlx::Error> for Error {      fn from(e: sqlx::Error) -> Self { diff --git a/luz/src/lib.rs b/luz/src/lib.rs index 4c5a841..1fb58d6 100644 --- a/luz/src/lib.rs +++ b/luz/src/lib.rs @@ -4,27 +4,31 @@ use std::{      sync::Arc,  }; +use chat::{Body, Message};  use connection::{write::WriteMessage, SupervisorSender};  use jabber::JID; +use presence::{Offline, Online, Presence}; +use roster::Contact;  use sqlx::SqlitePool; -use stanza::{ -    client::{ -        iq::{self, Iq, IqType}, -        Stanza, -    }, -    roster::{self, Query}, +use stanza::client::{ +    iq::{self, Iq, IqType}, +    Stanza,  };  use tokio::{      sync::{mpsc, oneshot, Mutex},      task::JoinSet,  }; +use uuid::Uuid;  use crate::connection::write::WriteHandle;  use crate::connection::{SupervisorCommand, SupervisorHandle};  use crate::error::Error; +mod chat;  mod connection;  mod error; +mod presence; +mod roster;  pub struct Luz {      receiver: mpsc::Receiver<CommandMessage>, @@ -190,7 +194,7 @@ impl CommandMessage {                      to: None,                      r#type: IqType::Get,                      lang: None, -                    query: Some(iq::Query::Roster(roster::Query { +                    query: Some(iq::Query::Roster(stanza::roster::Query {                          ver: None,                          items: Vec::new(),                      })), @@ -265,16 +269,62 @@ impl LuzHandle {  }  pub enum CommandMessage { +    /// connect to XMPP chat server. gets roster and      Connect, +    /// disconnect from XMPP chat server.      Disconnect, -    /// gets the roster. if offline, retreives cached version from database. should be stored in application memory. +    /// get the roster. if offline, retreive cached version from database. should be stored in application memory      GetRoster, -    SendMessage(JID, String), +    // add a contact to your roster, with a status of none, no subscriptions +    AddContact(JID), +    /// send a friend request i.e. a subscription request with a subscription pre-approval. if not already added to roster server adds to roster. +    BuddyRequest(JID), +    /// send a subscription request, without pre-approval. if not already added to roster server adds to roster. +    SubscriptionRequest(JID), +    /// accept a friend request by accepting a pending subscription and sending a subscription request back. if not already added to roster adds to roster. +    AcceptBuddyRequest(JID), +    /// accept a pending subscription and doesn't send a subscription request back. if not already added to roster adds to roster. +    AcceptSubscriptionRequest(JID), +    /// unsubscribe to a contact, but don't remove their subscription. +    UnsubscribeFromContact(JID), +    /// stop a contact from being subscribed, but stay subscribed to the contact. +    UnsubscribeContact(JID), +    /// remove subscriptions to and from contact, but keep in roster. +    UnfriendContact(JID), +    /// remove a contact from the contact list. will remove subscriptions if not already done then delete contact from roster. +    DeleteContact(JID), +    /// set online status +    SendStatus(Online), +    SendOffline(Offline), +    /// send a directed presence (usually to a non-contact). +    // TODO: should probably make it so people can add non-contact auto presence sharing in the client. +    SendDirectedPresence { +        to: JID, +        presence: Presence, +    }, +    SendMessage { +        id: Uuid, +        to: JID, +        body: Body, +    },  }  #[derive(Debug)]  pub enum UpdateMessage {      Error(Error), -    Connected, -    Roster(Vec<roster::Item>), +    Connected(Online), +    Disconnected(Offline), +    /// full roster (replace full app roster state with this) +    Roster(Vec<Contact>), +    /// roster update (only update app roster state) +    RosterPush(Contact), +    Presence { +        from: JID, +        presence: Presence, +    }, +    MessageDispatched(Uuid), +    Message { +        from: JID, +        message: Message, +    },  } diff --git a/luz/src/presence.rs b/luz/src/presence.rs new file mode 100644 index 0000000..0423d52 --- /dev/null +++ b/luz/src/presence.rs @@ -0,0 +1,19 @@ +use stanza::client::presence::Show; + +#[derive(Debug)] +pub struct Online { +    show: Option<Show>, +    status: Option<String>, +    priority: Option<i8>, +} + +#[derive(Debug)] +pub struct Offline { +    status: Option<String>, +} + +#[derive(Debug)] +pub enum Presence { +    Online(Online), +    Offline(Offline), +} diff --git a/luz/src/roster.rs b/luz/src/roster.rs new file mode 100644 index 0000000..c4502a0 --- /dev/null +++ b/luz/src/roster.rs @@ -0,0 +1,29 @@ +use std::collections::HashSet; + +use jid::JID; +use uuid::Uuid; + +#[derive(Debug)] +pub struct Contact { +    // jid is the id used to reference everything, but not the primary key +    jid: JID, +    subscription: Subscription, +    /// client user defined name +    name: Option<String>, +    // TODO: avatar, nickname +    /// nickname picked by contact +    // nickname: Option<String>, +    groups: HashSet<String>, +} + +#[derive(Debug)] +enum Subscription { +    None, +    PendingOut, +    PendingIn, +    OnlyOut, +    OnlyIn, +    OutPendingIn, +    InPendingOut, +    Buddy, +} | 
