aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.helix/languages.toml4
-rw-r--r--filamento/src/caps.rs2
-rw-r--r--filamento/src/chat.rs10
-rw-r--r--filamento/src/db.rs247
-rw-r--r--filamento/src/error.rs9
-rw-r--r--filamento/src/lib.rs126
-rw-r--r--filamento/src/logic/local_only.rs14
-rw-r--r--filamento/src/logic/mod.rs8
-rw-r--r--filamento/src/logic/offline.rs23
-rw-r--r--filamento/src/logic/online.rs101
-rw-r--r--filamento/src/logic/process_stanza.rs107
-rw-r--r--filamento/src/roster.rs4
-rw-r--r--filamento/src/user.rs4
-rw-r--r--jid/src/lib.rs291
-rw-r--r--lampada/src/connection/mod.rs27
-rw-r--r--lampada/src/error.rs2
-rw-r--r--lampada/src/lib.rs40
-rw-r--r--luz/src/client/tcp.rs19
-rw-r--r--luz/src/client/ws.rs11
-rw-r--r--luz/src/connection/tcp.rs2
-rw-r--r--luz/src/error.rs4
-rw-r--r--luz/src/jabber_stream.rs23
-rw-r--r--stanza/src/bind.rs4
-rw-r--r--stanza/src/rfc_7395.rs10
-rw-r--r--stanza/src/roster.rs4
-rw-r--r--stanza/src/stream.rs17
-rw-r--r--stanza/src/xep_0060/owner.rs4
27 files changed, 655 insertions, 462 deletions
diff --git a/.helix/languages.toml b/.helix/languages.toml
index 7662fff..02fab3a 100644
--- a/.helix/languages.toml
+++ b/.helix/languages.toml
@@ -1,9 +1,9 @@
[language-server.rust-analyzer]
command = "rust-analyzer"
-environment = { "DATABASE_URL" = "sqlite://filamento/filamento.db" }
+# environment = { "DATABASE_URL" = "sqlite://filamento/filamento.db" }
# checkOnSave.overrideCommand = "cargo check --message-format=json -p luz",
# check.overrideCommand="cargo check --message-format=json -p luz",
# check.workspace = false,
# cargo.target = "wasm32-unknown-unknown",
-config = { cargo.target = "wasm32-unknown-unknown", cargo.features = ["jid/rusqlite", "stanza/rfc_6121", "stanza/xep_0203", "stanza/rfc_7395", "stanza/xep_0030", "stanza/xep_0060", "stanza/xep_0172", "stanza/xep_0390", "stanza/xep_0128", "stanza/xep_0115", "stanza/xep_0084", "uuid/v4", "tokio/full", "rsasl/provider_base64", "rsasl/plain", "rsasl/config_builder", "rsasl/scram-sha-1", "stanza/xep_0156"] }
+# config = { cargo.target = "wasm32-unknown-unknown", cargo.features = ["jid/rusqlite", "stanza/rfc_6121", "stanza/xep_0203", "stanza/rfc_7395", "stanza/xep_0030", "stanza/xep_0060", "stanza/xep_0172", "stanza/xep_0390", "stanza/xep_0128", "stanza/xep_0115", "stanza/xep_0084", "uuid/v4", "tokio/full", "rsasl/provider_base64", "rsasl/plain", "rsasl/config_builder", "rsasl/scram-sha-1", "stanza/xep_0156"] }
# "sqlx/sqlite", "sqlx/runtime-tokio", "sqlx/uuid", "sqlx/chrono", "jid/sqlx",
diff --git a/filamento/src/caps.rs b/filamento/src/caps.rs
index 43f1cf4..e0587ff 100644
--- a/filamento/src/caps.rs
+++ b/filamento/src/caps.rs
@@ -377,7 +377,7 @@ pub fn node_to_hash(node: String) -> Result<Hash, HashNodeConversionError> {
#[cfg(test)]
mod tests {
- use peanuts::{Writer, element::IntoElement, loggable::Loggable};
+ use peanuts::Writer;
use stanza::{
xep_0004::{Field, FieldType, Value, X, XType},
xep_0030::info::{Feature, Identity},
diff --git a/filamento/src/chat.rs b/filamento/src/chat.rs
index 5f58866..c02654f 100644
--- a/filamento/src/chat.rs
+++ b/filamento/src/chat.rs
@@ -1,7 +1,7 @@
use std::fmt::{Display, Write};
use chrono::{DateTime, Utc};
-use jid::JID;
+use jid::{BareJID, JID};
use rusqlite::{
ToSql,
types::{FromSql, ToSqlOutput, Value},
@@ -15,7 +15,7 @@ pub struct Message {
pub id: Uuid,
// does not contain full user information
// bare jid (for now)
- pub from: JID,
+ pub from: BareJID,
pub delivery: Option<Delivery>,
pub timestamp: DateTime<Utc>,
// TODO: originally_from
@@ -97,7 +97,7 @@ pub struct Body {
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "reactive_stores", derive(reactive_stores::Store))]
pub struct Chat {
- pub correspondent: JID,
+ pub correspondent: BareJID,
pub have_chatted: bool,
// pub unread_messages: i32,
// pub latest_message: Message,
@@ -109,13 +109,13 @@ pub struct Chat {
pub enum ChatUpdate {}
impl Chat {
- pub fn new(correspondent: JID, have_chatted: bool) -> Self {
+ pub fn new(correspondent: BareJID, have_chatted: bool) -> Self {
Self {
correspondent,
have_chatted,
}
}
- pub fn correspondent(&self) -> &JID {
+ pub fn correspondent(&self) -> &BareJID {
&self.correspondent
}
}
diff --git a/filamento/src/db.rs b/filamento/src/db.rs
index 1b5afe6..385382a 100644
--- a/filamento/src/db.rs
+++ b/filamento/src/db.rs
@@ -2,7 +2,7 @@ use core::fmt::Display;
use std::{collections::HashSet, ops::Deref, path::Path, sync::Arc};
use chrono::{DateTime, Utc};
-use jid::JID;
+use jid::{BareJID, FullJID, JID};
use rusqlite::{Connection, OptionalExtension};
use tokio::sync::{Mutex, MutexGuard};
use tokio::sync::{mpsc, oneshot};
@@ -58,53 +58,22 @@ impl Db {
pub async fn create_connect_and_migrate(
path: impl AsRef<Path> + Send,
) -> Result<Self, DatabaseOpenError> {
- let (sender, receiver) = mpsc::channel(20);
- let (result_send, result_recv) = oneshot::channel();
- spawn_blocking(move || {
- let result = DbActor::new(path, receiver).await;
- match result {
- Ok(a) => {
- result_send.send(Ok(()));
- a.run()
- }
- Err(e) => {
- result_send.send(Err(e));
- }
- }
- });
- match result_recv.await {
- Ok(r) => match r {
- Ok(o) => Ok(Self { sender }),
- Err(e) => return Err(e),
- },
- Err(e) => return Err(e.into()),
- }
+ let (sender, receiver) = mpsc::unbounded_channel();
+
+ let actor = DbActor::new(path, receiver)?;
+ spawn_blocking(move || actor.run());
+
+ Ok(Self { sender })
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn create_connect_and_migrate_memory() -> Result<Self, DatabaseOpenError> {
- let (sender, receiver) = mpsc::channel(20);
- let (result_send, result_recv) = oneshot::channel();
- spawn_blocking(move || {
- let result = DbActor::new_memory(receiver).await;
- match result {
- Ok(a) => {
- result_send.send(Ok(()));
- // TODO: async run when not wasm
- a.run()
- }
- Err(e) => {
- result_send.send(Err(e));
- }
- }
- });
- match result_recv.await {
- Ok(r) => match r {
- Ok(o) => Ok(Self { sender }),
- Err(e) => return Err(e),
- },
- Err(e) => return Err(e.into()),
- }
+ let (sender, receiver) = mpsc::unbounded_channel();
+
+ let actor = DbActor::new_memory(receiver)?;
+ spawn_blocking(move || actor.run());
+
+ Ok(Self { sender })
}
/// `file_name` should be a file not in a directory
@@ -183,7 +152,7 @@ impl Db {
}
// TODO: this is not a 'read' user
- pub(crate) async fn read_user(&self, user: JID) -> Result<User, Error> {
+ pub(crate) async fn read_user(&self, user: BareJID) -> Result<User, Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::ReadUser { user, result };
self.sender.send(command);
@@ -192,7 +161,7 @@ impl Db {
}
/// returns whether or not the nickname was updated
- pub(crate) async fn delete_user_nick(&self, jid: JID) -> Result<bool, Error> {
+ pub(crate) async fn delete_user_nick(&self, jid: BareJID) -> Result<bool, Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::DeleteUserNick { jid, result };
self.sender.send(command);
@@ -201,7 +170,7 @@ impl Db {
}
/// returns whether or not the nickname was updated
- pub(crate) async fn upsert_user_nick(&self, jid: JID, nick: String) -> Result<bool, Error> {
+ pub(crate) async fn upsert_user_nick(&self, jid: BareJID, nick: String) -> Result<bool, Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::UpsertUserNick { jid, nick, result };
self.sender.send(command);
@@ -212,7 +181,7 @@ impl Db {
/// returns whether or not the avatar was updated, and the file to delete if there existed an old avatar
pub(crate) async fn delete_user_avatar(
&self,
- jid: JID,
+ jid: BareJID,
) -> Result<(bool, Option<String>), Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::DeleteUserAvatar { jid, result };
@@ -224,7 +193,7 @@ impl Db {
/// returns whether or not the avatar was updated, and the file to delete if there existed an old avatar
pub(crate) async fn upsert_user_avatar(
&self,
- jid: JID,
+ jid: BareJID,
avatar: String,
) -> Result<(bool, Option<String>), Error> {
let (result, recv) = oneshot::channel();
@@ -259,7 +228,7 @@ impl Db {
result
}
- pub(crate) async fn read_contact(&self, contact: JID) -> Result<Contact, Error> {
+ pub(crate) async fn read_contact(&self, contact: BareJID) -> Result<Contact, Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::ReadContact { contact, result };
self.sender.send(command);
@@ -267,7 +236,10 @@ impl Db {
result
}
- pub(crate) async fn read_contact_opt(&self, contact: JID) -> Result<Option<Contact>, Error> {
+ pub(crate) async fn read_contact_opt(
+ &self,
+ contact: BareJID,
+ ) -> Result<Option<Contact>, Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::ReadContactOpt { contact, result };
self.sender.send(command);
@@ -292,7 +264,7 @@ impl Db {
result
}
- pub(crate) async fn delete_contact(&self, contact: JID) -> Result<(), Error> {
+ pub(crate) async fn delete_contact(&self, contact: BareJID) -> Result<(), Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::DeleteContact { contact, result };
self.sender.send(command);
@@ -336,7 +308,7 @@ impl Db {
// TODO: what happens if a correspondent changes from a user to a contact? maybe just have correspondent be a user, then have the client make the user show up as a contact in ui if they are in the loaded roster.
- pub(crate) async fn read_chat(&self, chat: JID) -> Result<Chat, Error> {
+ pub(crate) async fn read_chat(&self, chat: BareJID) -> Result<Chat, Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::ReadChat { chat, result };
self.sender.send(command);
@@ -344,7 +316,7 @@ impl Db {
result
}
- pub(crate) async fn read_chat_and_user(&self, chat: JID) -> Result<(Chat, User), Error> {
+ pub(crate) async fn read_chat_and_user(&self, chat: BareJID) -> Result<(Chat, User), Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::ReadChatAndUser { chat, result };
self.sender.send(command);
@@ -352,7 +324,7 @@ impl Db {
result
}
- pub(crate) async fn mark_chat_as_chatted(&self, chat: JID) -> Result<(), Error> {
+ pub(crate) async fn mark_chat_as_chatted(&self, chat: BareJID) -> Result<(), Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::MarkChatAsChatted { chat, result };
self.sender.send(command);
@@ -363,7 +335,7 @@ impl Db {
pub(crate) async fn update_chat_correspondent(
&self,
old_chat: Chat,
- new_correspondent: JID,
+ new_correspondent: BareJID,
) -> Result<Chat, Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::UpdateChatCorrespondent {
@@ -378,7 +350,7 @@ impl Db {
// pub(crate) async fn update_chat
- pub(crate) async fn delete_chat(&self, chat: JID) -> Result<(), Error> {
+ pub(crate) async fn delete_chat(&self, chat: BareJID) -> Result<(), Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::DeleteChat { chat, result };
self.sender.send(command);
@@ -434,8 +406,8 @@ impl Db {
pub(crate) async fn create_message(
&self,
message: Message,
- chat: JID,
- from: JID,
+ chat: BareJID,
+ from: FullJID,
) -> Result<(), Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::CreateMessage {
@@ -449,7 +421,7 @@ impl Db {
result
}
- pub(crate) async fn upsert_chat_and_user(&self, chat: JID) -> Result<bool, Error> {
+ pub(crate) async fn upsert_chat_and_user(&self, chat: BareJID) -> Result<bool, Error> {
let (result, recv) = oneshot::channel();
let command = DbCommand::UpsertChatAndUser { chat, result };
self.sender.send(command);
@@ -463,10 +435,8 @@ impl Db {
&self,
message: Message,
// TODO: enforce two kinds of jid. bare and full
- // must be bare jid
- chat: JID,
- // full jid
- from: JID,
+ chat: BareJID,
+ from: FullJID,
) -> Result<(), Error> {
tracing::info!("MSGDEBUG create_message_with_user_resource exists");
let (result, recv) = oneshot::channel();
@@ -514,9 +484,9 @@ impl Db {
DeleteCachedStatus => delete_cached_status() -> ();
UpsertCachedStatus => upsert_cached_status(status: Online) -> ();
ReadCachedStatus => read_cached_status() -> Online;
- ReadMessageHistoryWithUsers => read_message_history_with_users(chat: JID) -> Vec<(Message, User)>;
+ ReadMessageHistoryWithUsers => read_message_history_with_users(chat: BareJID) -> Vec<(Message, User)>;
// TODO: paging
- ReadMessageHistory => read_message_history(chat: JID) -> Vec<Message>;
+ ReadMessageHistory => read_message_history(chat: BareJID) -> Vec<Message>;
ReadMessage => read_message(message: Uuid) -> Message;
DeleteMessage => delete_message(message: Uuid) -> ()
);
@@ -546,24 +516,24 @@ pub enum DbCommand {
result: oneshot::Sender<Result<(), Error>>,
},
ReadUser {
- user: JID,
+ user: BareJID,
result: oneshot::Sender<Result<User, Error>>,
},
DeleteUserNick {
- jid: JID,
+ jid: BareJID,
result: oneshot::Sender<Result<bool, Error>>,
},
UpsertUserNick {
- jid: JID,
+ jid: BareJID,
nick: String,
result: oneshot::Sender<Result<bool, Error>>,
},
DeleteUserAvatar {
- jid: JID,
+ jid: BareJID,
result: oneshot::Sender<Result<(bool, Option<String>), Error>>,
},
UpsertUserAvatar {
- jid: JID,
+ jid: BareJID,
avatar: String,
result: oneshot::Sender<Result<(bool, Option<String>), Error>>,
},
@@ -576,11 +546,11 @@ pub enum DbCommand {
result: oneshot::Sender<Result<(), Error>>,
},
ReadContact {
- contact: JID,
+ contact: BareJID,
result: oneshot::Sender<Result<Contact, Error>>,
},
ReadContactOpt {
- contact: JID,
+ contact: BareJID,
result: oneshot::Sender<Result<Option<Contact>, Error>>,
},
UpdateContact {
@@ -592,7 +562,7 @@ pub enum DbCommand {
result: oneshot::Sender<Result<(), Error>>,
},
DeleteContact {
- contact: JID,
+ contact: BareJID,
result: oneshot::Sender<Result<(), Error>>,
},
ReplaceCachedRoster {
@@ -610,24 +580,24 @@ pub enum DbCommand {
result: oneshot::Sender<Result<(), Error>>,
},
ReadChat {
- chat: JID,
+ chat: BareJID,
result: oneshot::Sender<Result<Chat, Error>>,
},
ReadChatAndUser {
- chat: JID,
+ chat: BareJID,
result: oneshot::Sender<Result<(Chat, User), Error>>,
},
MarkChatAsChatted {
- chat: JID,
+ chat: BareJID,
result: oneshot::Sender<Result<(), Error>>,
},
UpdateChatCorrespondent {
old_chat: Chat,
- new_correspondent: JID,
+ new_correspondent: BareJID,
result: oneshot::Sender<Result<Chat, Error>>,
},
DeleteChat {
- chat: JID,
+ chat: BareJID,
result: oneshot::Sender<Result<(), Error>>,
},
ReadChats {
@@ -652,18 +622,18 @@ pub enum DbCommand {
// },
CreateMessage {
message: Message,
- chat: JID,
- from: JID,
+ chat: BareJID,
+ from: FullJID,
result: oneshot::Sender<Result<(), Error>>,
},
UpsertChatAndUser {
- chat: JID,
+ chat: BareJID,
result: oneshot::Sender<Result<bool, Error>>,
},
CreateMessageWithUserResource {
message: Message,
- chat: JID,
- from: JID,
+ chat: BareJID,
+ from: FullJID,
result: oneshot::Sender<Result<(), Error>>,
},
UpdateMessageDelivery {
@@ -680,11 +650,11 @@ pub enum DbCommand {
result: oneshot::Sender<Result<Message, Error>>,
},
ReadMessageHistory {
- chat: JID,
+ chat: BareJID,
result: oneshot::Sender<Result<Vec<Message>, Error>>,
},
ReadMessageHistoryWithUsers {
- chat: JID,
+ chat: BareJID,
result: oneshot::Sender<Result<Vec<(Message, User)>, Error>>,
},
ReadCachedStatus {
@@ -758,17 +728,19 @@ impl Display for DbCommand {
impl DbActor {
/// must be run in blocking spawn
#[cfg(not(target_arch = "wasm32"))]
- pub(crate) fn new(path: impl AsRef<Path>, receiver: mpsc::Receiver<DbCommand>) -> Self {
+ pub(crate) fn new(
+ path: impl AsRef<Path>,
+ receiver: mpsc::UnboundedReceiver<DbCommand>,
+ ) -> Result<Self, DatabaseOpenError> {
if let Some(dir) = path.as_ref().parent() {
if dir.is_dir() {
} else {
- tokio::fs::create_dir_all(dir).await?;
+ std::fs::create_dir_all(dir)?;
}
- let _file = tokio::fs::OpenOptions::new()
+ let _file = std::fs::OpenOptions::new()
.append(true)
.create(true)
- .open(path.as_ref())
- .await?;
+ .open(path.as_ref())?;
}
let url = format!(
"{}",
@@ -786,7 +758,9 @@ impl DbActor {
/// must be run in blocking spawn
#[cfg(not(target_arch = "wasm32"))]
- pub(crate) fn new_memory(receiver: mpsc::Receiver<DbCommand>) -> Self {
+ pub(crate) fn new_memory(
+ receiver: mpsc::UnboundedReceiver<DbCommand>,
+ ) -> Result<Self, DatabaseOpenError> {
let db = Connection::open_in_memory()?;
db.execute_batch(include_str!("../migrations/1.sql"))?;
Ok(Self { db, receiver })
@@ -850,7 +824,7 @@ impl DbActor {
result.send(self.read_contact(contact));
}
DbCommand::ReadContactOpt { contact, result } => {
- result.send(self.read_contact_opt(&contact));
+ result.send(self.read_contact_opt(contact));
}
DbCommand::UpdateContact { contact, result } => {
result.send(self.update_contact(contact));
@@ -978,7 +952,7 @@ impl DbActor {
}
// TODO: this is not a 'read' user
- pub(crate) fn read_user(&self, user: JID) -> Result<User, Error> {
+ pub(crate) fn read_user(&self, user: BareJID) -> Result<User, Error> {
let db = &self.db;
let user_opt = db
.query_row(
@@ -1007,7 +981,7 @@ impl DbActor {
}
/// returns whether or not the nickname was updated
- pub(crate) fn delete_user_nick(&self, jid: JID) -> Result<bool, Error> {
+ pub(crate) fn delete_user_nick(&self, jid: BareJID) -> Result<bool, Error> {
let rows_affected;
{
rows_affected = self.db.execute("insert into users (jid, nick) values (?1, ?2) on conflict do update set nick = ?3 where nick is not ?4", (jid, None::<String>, None::<String>, None::<String>))?;
@@ -1020,7 +994,7 @@ impl DbActor {
}
/// returns whether or not the nickname was updated
- pub(crate) fn upsert_user_nick(&self, jid: JID, nick: String) -> Result<bool, Error> {
+ pub(crate) fn upsert_user_nick(&self, jid: BareJID, nick: String) -> Result<bool, Error> {
let rows_affected;
{
rows_affected = self.db.execute("insert into users (jid, nick) values (?1, ?2) on conflict do update set nick = ?3 where nick is not ?4", (jid, &nick, &nick, &nick))?;
@@ -1033,7 +1007,7 @@ impl DbActor {
}
/// returns whether or not the avatar was updated, and the file to delete if there existed an old avatar
- pub(crate) fn delete_user_avatar(&self, jid: JID) -> Result<(bool, Option<String>), Error> {
+ pub(crate) fn delete_user_avatar(&self, jid: BareJID) -> Result<(bool, Option<String>), Error> {
let (old_avatar, rows_affected): (Option<String>, _);
{
let db = &self.db;
@@ -1054,7 +1028,7 @@ impl DbActor {
/// returns whether or not the avatar was updated, and the file to delete if there existed an old avatar
pub(crate) fn upsert_user_avatar(
&self,
- jid: JID,
+ jid: BareJID,
avatar: String,
) -> Result<(bool, Option<String>), Error> {
let (old_avatar, rows_affected): (Option<String>, _);
@@ -1108,7 +1082,7 @@ impl DbActor {
Ok(())
}
- pub(crate) fn read_contact(&self, contact: JID) -> Result<Contact, Error> {
+ pub(crate) fn read_contact(&self, contact: BareJID) -> Result<Contact, Error> {
let db = &self.db;
let mut contact_item = db.query_row(
"select user_jid, name, subscription from roster where user_jid = ?1",
@@ -1130,7 +1104,7 @@ impl DbActor {
Ok(contact_item)
}
- pub(crate) fn read_contact_opt(&self, contact: &JID) -> Result<Option<Contact>, Error> {
+ pub(crate) fn read_contact_opt(&self, contact: BareJID) -> Result<Option<Contact>, Error> {
let db = &self.db;
let contact_item = db
.query_row(
@@ -1210,7 +1184,7 @@ impl DbActor {
Ok(())
}
- pub(crate) fn delete_contact(&self, contact: JID) -> Result<(), Error> {
+ pub(crate) fn delete_contact(&self, contact: BareJID) -> Result<(), Error> {
self.db
.execute("delete from roster where user_jid = ?1", [&contact])?;
Ok(())
@@ -1288,19 +1262,21 @@ impl DbActor {
// TODO: what happens if a correspondent changes from a user to a contact? maybe just have correspondent be a user, then have the client make the user show up as a contact in ui if they are in the loaded roster.
- /// should be a bare jid
/// TODO: this is NOT a read
- pub(crate) fn read_chat(&self, chat: JID) -> Result<Chat, Error> {
- let chat_opt = self.db.query_row(
- "select correspondent, have_chatted from chats where correspondent = ?1",
- [&chat],
- |row| {
- Ok(Chat {
- correspondent: row.get(0)?,
- have_chatted: row.get(1)?,
- })
- },
- ).optional()?;
+ pub(crate) fn read_chat(&self, chat: BareJID) -> Result<Chat, Error> {
+ let chat_opt = self
+ .db
+ .query_row(
+ "select correspondent, have_chatted from chats where correspondent = ?1",
+ [&chat],
+ |row| {
+ Ok(Chat {
+ correspondent: row.get(0)?,
+ have_chatted: row.get(1)?,
+ })
+ },
+ )
+ .optional()?;
match chat_opt {
Some(chat) => return Ok(chat),
None => {
@@ -1314,7 +1290,7 @@ impl DbActor {
}
}
- pub(crate) fn read_chat_and_user(&self, chat: JID) -> Result<(Chat, User), Error> {
+ pub(crate) fn read_chat_and_user(&self, chat: BareJID) -> Result<(Chat, User), Error> {
let user = self.read_user(chat.clone())?;
let chat_opt = self.db.query_row(
"select correspondent, have_chatted, jid, nick, avatar from chats join users on correspondent = jid where correspondent = ?1",
@@ -1346,7 +1322,7 @@ impl DbActor {
}
}
- pub(crate) fn mark_chat_as_chatted(&self, chat: JID) -> Result<(), Error> {
+ pub(crate) fn mark_chat_as_chatted(&self, chat: BareJID) -> Result<(), Error> {
self.db.execute(
"update chats set have_chatted = true where correspondent = ?1",
[chat],
@@ -1357,7 +1333,7 @@ impl DbActor {
pub(crate) fn update_chat_correspondent(
&self,
old_chat: Chat,
- new_correspondent: JID,
+ new_correspondent: BareJID,
) -> Result<Chat, Error> {
let new_jid = &new_correspondent;
let old_jid = old_chat.correspondent();
@@ -1374,7 +1350,7 @@ impl DbActor {
// pub(crate) fn update_chat
- pub(crate) fn delete_chat(&self, chat: JID) -> Result<(), Error> {
+ pub(crate) fn delete_chat(&self, chat: BareJID) -> Result<(), Error> {
self.db
.execute("delete from chats where correspondent = ?1", [chat])?;
Ok(())
@@ -1484,7 +1460,7 @@ impl DbActor {
}
#[tracing::instrument]
- fn read_chat_id(&self, chat: JID) -> Result<Uuid, Error> {
+ fn read_chat_id(&self, chat: BareJID) -> Result<Uuid, Error> {
let chat_id = self.db.query_row(
"select id from chats where correspondent = ?1",
[chat],
@@ -1510,8 +1486,8 @@ impl DbActor {
pub(crate) fn create_message(
&self,
message: Message,
- chat: JID,
- from: JID,
+ chat: BareJID,
+ from: FullJID,
) -> Result<(), Error> {
let from_jid = from.as_bare();
let chat_id = self.read_chat_id(chat)?;
@@ -1520,18 +1496,17 @@ impl DbActor {
Ok(())
}
- pub(crate) fn upsert_chat_and_user(&self, chat: &JID) -> Result<bool, Error> {
- let bare_chat = chat.as_bare();
+ pub(crate) fn upsert_chat_and_user(&self, chat: &BareJID) -> Result<bool, Error> {
let db = &self.db;
db.execute(
"insert into users (jid) values (?1) on conflict do nothing",
- [&bare_chat],
+ [&chat],
)?;
let id = Uuid::new_v4();
- db.execute("insert into chats (id, correspondent, have_chatted) values (?1, ?2, ?3) on conflict do nothing", (id, &bare_chat, false))?;
+ db.execute("insert into chats (id, correspondent, have_chatted) values (?1, ?2, ?3) on conflict do nothing", (id, &chat, false))?;
let chat = db.query_row(
"select correspondent, have_chatted from chats where correspondent = ?1",
- [&bare_chat],
+ [&chat],
|row| {
Ok(Chat {
correspondent: row.get(0)?,
@@ -1547,21 +1522,15 @@ impl DbActor {
pub(crate) fn create_message_with_user_resource(
&self,
message: Message,
- // TODO: enforce two kinds of jid. bare and full
- // must be bare jid
- chat: JID,
- // full jid
- from: JID,
+ chat: BareJID,
+ from: FullJID,
) -> Result<(), Error> {
let from_jid = from.as_bare();
- let chat = chat.as_bare();
tracing::debug!("creating resource");
- if let Some(resource) = &from.resourcepart {
- self.db.execute(
- "insert into resources (bare_jid, resource) values (?1, ?2) on conflict do nothing",
- (&from_jid, resource),
- )?;
- }
+ self.db.execute(
+ "insert into resources (bare_jid, resource) values (?1, ?2) on conflict do nothing",
+ (&from_jid, &from.resourcepart),
+ )?;
self.create_message(message, chat, from)?;
Ok(())
}
@@ -1615,7 +1584,7 @@ impl DbActor {
}
// TODO: paging
- pub(crate) fn read_message_history(&self, chat: JID) -> Result<Vec<Message>, Error> {
+ pub(crate) fn read_message_history(&self, chat: BareJID) -> Result<Vec<Message>, Error> {
let chat_id = self.read_chat_id(chat)?;
let messages = self
.db
@@ -1638,7 +1607,7 @@ impl DbActor {
pub(crate) fn read_message_history_with_users(
&self,
- chat: JID,
+ chat: BareJID,
) -> Result<Vec<(Message, User)>, Error> {
let chat_id = self.read_chat_id(chat)?;
let messages = self
diff --git a/filamento/src/error.rs b/filamento/src/error.rs
index af3320f..721d532 100644
--- a/filamento/src/error.rs
+++ b/filamento/src/error.rs
@@ -3,6 +3,7 @@ use std::{num::TryFromIntError, string::FromUtf8Error, sync::Arc};
use base64::DecodeError;
use image::ImageError;
use jid::JID;
+use jid::JIDError;
use lampada::error::{ActorError, ReadError, WriteError};
use stanza::client::{Stanza, iq::Query};
use thiserror::Error;
@@ -297,7 +298,7 @@ pub enum DatabaseOpenError {
// #[error("migration: {0}")]
// Migration(Arc<rusqlite::migrate::MigrateError>),
#[error("io: {0}")]
- Io(Arc<tokio::io::Error>),
+ Io(Arc<std::io::Error>),
#[error("invalid path")]
InvalidPath,
#[error("tokio oneshot recv error: {0}")]
@@ -310,8 +311,8 @@ pub enum DatabaseOpenError {
// }
// }
-impl From<tokio::io::Error> for DatabaseOpenError {
- fn from(e: tokio::io::Error) -> Self {
+impl From<std::io::Error> for DatabaseOpenError {
+ fn from(e: std::io::Error) -> Self {
Self::Io(Arc::new(e))
}
}
@@ -332,6 +333,8 @@ pub enum PresenceError {
MissingFrom,
#[error("stanza error: {0:?}")]
StanzaError(Option<stanza::client::error::Error>),
+ #[error("received subscription request from a non-bare jid")]
+ InvalidSubscriptionRequest(#[from] JIDError),
}
#[derive(Debug, Error, Clone)]
diff --git a/filamento/src/lib.rs b/filamento/src/lib.rs
index 068bfe8..d3033b7 100644
--- a/filamento/src/lib.rs
+++ b/filamento/src/lib.rs
@@ -16,7 +16,7 @@ use error::{
};
use files::FileStore;
use futures::FutureExt;
-use jid::JID;
+use jid::{BareJID, JID};
use lampada::{
Connected, CoreClient, CoreClientCommand, Logic, SupervisorSender, WriteMessage,
error::{ActorError, CommandError, ConnectionError, ReadError, WriteError},
@@ -68,45 +68,55 @@ pub enum Command<Fs: FileStore> {
oneshot::Sender<Result<Vec<((Chat, User), (Message, User))>, DatabaseError>>,
),
/// get a specific chat by jid
- GetChat(JID, oneshot::Sender<Result<Chat, DatabaseError>>),
+ GetChat(BareJID, oneshot::Sender<Result<Chat, DatabaseError>>),
/// get a specific chat and user by jid
- GetChatAndUser(JID, oneshot::Sender<Result<(Chat, User), DatabaseError>>),
+ GetChatAndUser(
+ BareJID,
+ oneshot::Sender<Result<(Chat, User), DatabaseError>>,
+ ),
/// get message history for chat (does appropriate mam things)
GetMessage(Uuid, oneshot::Sender<Result<Message, DatabaseError>>),
// TODO: paging and filtering
- GetMessages(JID, oneshot::Sender<Result<Vec<Message>, DatabaseError>>),
+ GetMessages(
+ BareJID,
+ oneshot::Sender<Result<Vec<Message>, DatabaseError>>,
+ ),
/// get message history for chat (does appropriate mam things)
// TODO: paging and filtering
GetMessagesWithUsers(
- JID,
+ BareJID,
oneshot::Sender<Result<Vec<(Message, User)>, DatabaseError>>,
),
/// delete a chat from your chat history, along with all the corresponding messages
- DeleteChat(JID, oneshot::Sender<Result<(), DatabaseError>>),
+ DeleteChat(BareJID, oneshot::Sender<Result<(), DatabaseError>>),
/// delete a message from your chat history
DeleteMessage(Uuid, oneshot::Sender<Result<(), DatabaseError>>),
/// get a user from your users database
- GetUser(JID, oneshot::Sender<Result<User, DatabaseError>>),
+ GetUser(BareJID, oneshot::Sender<Result<User, DatabaseError>>),
/// add a contact to your roster, with a status of none, no subscriptions.
- AddContact(JID, oneshot::Sender<Result<(), RosterError>>),
+ AddContact(BareJID, oneshot::Sender<Result<(), RosterError>>),
/// 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, oneshot::Sender<Result<(), SubscribeError>>),
+ BuddyRequest(BareJID, oneshot::Sender<Result<(), SubscribeError>>),
/// send a subscription request, without pre-approval. if not already added to roster server adds to roster.
- SubscriptionRequest(JID, oneshot::Sender<Result<(), SubscribeError>>),
+ SubscriptionRequest(BareJID, oneshot::Sender<Result<(), SubscribeError>>),
/// 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, oneshot::Sender<Result<(), SubscribeError>>),
+ AcceptBuddyRequest(BareJID, oneshot::Sender<Result<(), SubscribeError>>),
/// accept a pending subscription and doesn't send a subscription request back. if not already added to roster adds to roster.
- AcceptSubscriptionRequest(JID, oneshot::Sender<Result<(), SubscribeError>>),
+ AcceptSubscriptionRequest(BareJID, oneshot::Sender<Result<(), SubscribeError>>),
/// unsubscribe to a contact, but don't remove their subscription.
- UnsubscribeFromContact(JID, oneshot::Sender<Result<(), WriteError>>),
+ UnsubscribeFromContact(BareJID, oneshot::Sender<Result<(), WriteError>>),
/// stop a contact from being subscribed, but stay subscribed to the contact.
- UnsubscribeContact(JID, oneshot::Sender<Result<(), WriteError>>),
+ UnsubscribeContact(BareJID, oneshot::Sender<Result<(), WriteError>>),
/// remove subscriptions to and from contact, but keep in roster.
- UnfriendContact(JID, oneshot::Sender<Result<(), WriteError>>),
+ UnfriendContact(BareJID, oneshot::Sender<Result<(), WriteError>>),
/// remove a contact from the contact list. will remove subscriptions if not already done then delete contact from roster.
- DeleteContact(JID, oneshot::Sender<Result<(), RosterError>>),
+ DeleteContact(BareJID, oneshot::Sender<Result<(), RosterError>>),
/// update contact. contact details will be overwritten with the contents of the contactupdate struct.
- UpdateContact(JID, ContactUpdate, oneshot::Sender<Result<(), RosterError>>),
+ UpdateContact(
+ BareJID,
+ ContactUpdate,
+ oneshot::Sender<Result<(), RosterError>>,
+ ),
/// set online status. if disconnected, will be cached so when client connects, will be sent as the initial presence.
SetStatus(Online, oneshot::Sender<Result<(), StatusError>>),
/// send presence stanza
@@ -120,7 +130,7 @@ pub enum Command<Fs: FileStore> {
// TODO: should probably make it so people can add non-contact auto presence sharing in the client (most likely through setting an internal setting)
/// 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),
+ SendMessage(BareJID, Body),
// TODO: resend failed messages
// ResendMessage(Uuid),
/// disco info query
@@ -146,7 +156,7 @@ pub enum Command<Fs: FileStore> {
sender: oneshot::Sender<Result<(), PEPError>>,
},
GetPEPItem {
- jid: Option<JID>,
+ jid: Option<BareJID>,
node: String,
id: String,
sender: oneshot::Sender<Result<pep::Item, PEPError>>,
@@ -171,7 +181,7 @@ pub enum UpdateMessage {
Offline(Offline),
/// (only update app roster state, don't replace)
RosterUpdate(Contact, User),
- RosterDelete(JID),
+ RosterDelete(BareJID),
/// presences should be stored with users in the ui, not contacts, as presences can be received from anyone
Presence {
from: JID,
@@ -180,22 +190,23 @@ pub enum UpdateMessage {
// TODO: receipts
// MessageDispatched(Uuid),
Message {
- to: JID,
+ // TODO: rename to chat?
+ to: BareJID,
from: User,
message: Message,
},
MessageDelivery {
id: Uuid,
- chat: JID,
+ chat: BareJID,
delivery: Delivery,
},
- SubscriptionRequest(jid::JID),
+ SubscriptionRequest(BareJID),
NickChanged {
- jid: JID,
+ jid: BareJID,
nick: Option<String>,
},
AvatarChanged {
- jid: JID,
+ jid: BareJID,
id: Option<String>,
},
}
@@ -259,7 +270,7 @@ impl<Fs: FileStore + Clone + Send + Sync + 'static> Client<Fs> {
timeout: Duration::from_secs(20),
};
- let logic = ClientLogic::new(client.clone(), jid.as_bare(), db, update_send, file_store);
+ let logic = ClientLogic::new(client.clone(), jid.to_bare(), db, update_send, file_store);
let actor: CoreClient<ClientLogic<Fs>> =
CoreClient::new(jid, password, command_receiver, None, sup_recv, logic);
@@ -373,7 +384,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(chats)
}
- pub async fn get_chat(&self, jid: JID) -> Result<Chat, CommandError<DatabaseError>> {
+ pub async fn get_chat(&self, jid: BareJID) -> Result<Chat, CommandError<DatabaseError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::GetChat(jid, send)))
.await
@@ -385,12 +396,17 @@ impl<Fs: FileStore> Client<Fs> {
Ok(chat)
}
- pub async fn get_chat_and_user(&self, jid: JID) -> Result<(Chat, User), CommandError<DatabaseError>> {
+ pub async fn get_chat_and_user(
+ &self,
+ jid: BareJID,
+ ) -> Result<(Chat, User), CommandError<DatabaseError>> {
let (send, recv) = oneshot::channel();
- self.send(CoreClientCommand::Command(Command::GetChatAndUser(jid, send)))
- .await
- .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?;
- let result= timeout(self.timeout, recv)
+ self.send(CoreClientCommand::Command(Command::GetChatAndUser(
+ 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)))??;
@@ -411,7 +427,7 @@ impl<Fs: FileStore> Client<Fs> {
pub async fn get_messages(
&self,
- jid: JID,
+ jid: BareJID,
) -> Result<Vec<Message>, CommandError<DatabaseError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::GetMessages(jid, send)))
@@ -426,7 +442,7 @@ impl<Fs: FileStore> Client<Fs> {
pub async fn get_messages_with_users(
&self,
- jid: JID,
+ jid: BareJID,
) -> Result<Vec<(Message, User)>, CommandError<DatabaseError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::GetMessagesWithUsers(
@@ -441,7 +457,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(messages)
}
- pub async fn delete_chat(&self, jid: JID) -> Result<(), CommandError<DatabaseError>> {
+ pub async fn delete_chat(&self, jid: BareJID) -> Result<(), CommandError<DatabaseError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::DeleteChat(jid, send)))
.await
@@ -465,7 +481,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn get_user(&self, jid: JID) -> Result<User, CommandError<DatabaseError>> {
+ pub async fn get_user(&self, jid: BareJID) -> Result<User, CommandError<DatabaseError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::GetUser(jid, send)))
.await
@@ -477,7 +493,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn add_contact(&self, jid: JID) -> Result<(), CommandError<RosterError>> {
+ pub async fn add_contact(&self, jid: BareJID) -> Result<(), CommandError<RosterError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::AddContact(jid, send)))
.await
@@ -489,7 +505,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn buddy_request(&self, jid: JID) -> Result<(), CommandError<SubscribeError>> {
+ pub async fn buddy_request(&self, jid: BareJID) -> Result<(), CommandError<SubscribeError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::BuddyRequest(jid, send)))
.await
@@ -501,7 +517,10 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn subscription_request(&self, jid: JID) -> Result<(), CommandError<SubscribeError>> {
+ pub async fn subscription_request(
+ &self,
+ jid: BareJID,
+ ) -> Result<(), CommandError<SubscribeError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::SubscriptionRequest(
jid, send,
@@ -515,7 +534,10 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn accept_buddy_request(&self, jid: JID) -> Result<(), CommandError<SubscribeError>> {
+ pub async fn accept_buddy_request(
+ &self,
+ jid: BareJID,
+ ) -> Result<(), CommandError<SubscribeError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::AcceptBuddyRequest(
jid, send,
@@ -531,7 +553,7 @@ impl<Fs: FileStore> Client<Fs> {
pub async fn accept_subscription_request(
&self,
- jid: JID,
+ jid: BareJID,
) -> Result<(), CommandError<SubscribeError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(
@@ -546,7 +568,10 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn unsubscribe_from_contact(&self, jid: JID) -> Result<(), CommandError<WriteError>> {
+ pub async fn unsubscribe_from_contact(
+ &self,
+ jid: BareJID,
+ ) -> Result<(), CommandError<WriteError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::UnsubscribeFromContact(
jid, send,
@@ -560,7 +585,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn unsubscribe_contact(&self, jid: JID) -> Result<(), CommandError<WriteError>> {
+ pub async fn unsubscribe_contact(&self, jid: BareJID) -> Result<(), CommandError<WriteError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::UnsubscribeContact(
jid, send,
@@ -574,7 +599,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn unfriend_contact(&self, jid: JID) -> Result<(), CommandError<WriteError>> {
+ pub async fn unfriend_contact(&self, jid: BareJID) -> Result<(), CommandError<WriteError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::UnfriendContact(
jid, send,
@@ -588,7 +613,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn delete_contact(&self, jid: JID) -> Result<(), CommandError<RosterError>> {
+ pub async fn delete_contact(&self, jid: BareJID) -> Result<(), CommandError<RosterError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::DeleteContact(
jid, send,
@@ -604,7 +629,7 @@ impl<Fs: FileStore> Client<Fs> {
pub async fn update_contact(
&self,
- jid: JID,
+ jid: BareJID,
update: ContactUpdate,
) -> Result<(), CommandError<RosterError>> {
let (send, recv) = oneshot::channel();
@@ -632,7 +657,7 @@ impl<Fs: FileStore> Client<Fs> {
Ok(result)
}
- pub async fn send_message(&self, jid: JID, body: Body) -> Result<(), ActorError> {
+ pub async fn send_message(&self, jid: BareJID, body: Body) -> Result<(), ActorError> {
self.send(CoreClientCommand::Command(Command::SendMessage(jid, body)))
.await?;
Ok(())
@@ -711,7 +736,8 @@ impl<Fs: FileStore> Client<Fs> {
pub async fn get_pep_item(
&self,
- jid: Option<JID>,
+ // i think this is correct?, should not be able to send pep requests to a full jid.
+ jid: Option<BareJID>,
node: String,
id: String,
) -> Result<pep::Item, CommandError<PEPError>> {
@@ -811,7 +837,7 @@ mod tests {
info!("sending message");
client
.send_message(
- JID::from_str("cel@blos.sm").unwrap(),
+ BareJID::from_str("cel@blos.sm").unwrap(),
chat::Body {
body: "hallo!!!".to_string(),
},
@@ -821,7 +847,7 @@ mod tests {
info!("sent message");
client
.send_message(
- JID::from_str("cel@blos.sm").unwrap(),
+ BareJID::from_str("cel@blos.sm").unwrap(),
chat::Body {
body: "hallo 2".to_string(),
},
diff --git a/filamento/src/logic/local_only.rs b/filamento/src/logic/local_only.rs
index f5705f4..7f3a2e6 100644
--- a/filamento/src/logic/local_only.rs
+++ b/filamento/src/logic/local_only.rs
@@ -1,4 +1,4 @@
-use jid::JID;
+use jid::{BareJID, JID};
use uuid::Uuid;
use crate::{
@@ -39,14 +39,14 @@ pub async fn handle_get_chats_ordered_with_latest_messages_and_users<Fs: FileSto
pub async fn handle_get_chat<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
- jid: JID,
+ jid: BareJID,
) -> Result<Chat, DatabaseError> {
Ok(logic.db().read_chat(jid).await?)
}
pub async fn handle_get_chat_and_user<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
- jid: JID,
+ jid: BareJID,
) -> Result<(Chat, User), DatabaseError> {
Ok(logic.db().read_chat_and_user(jid).await?)
}
@@ -60,21 +60,21 @@ pub async fn handle_get_message<Fs: FileStore + Clone>(
pub async fn handle_get_messages<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
- jid: JID,
+ jid: BareJID,
) -> Result<Vec<Message>, DatabaseError> {
Ok(logic.db().read_message_history(jid).await?)
}
pub async fn handle_get_messages_with_users<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
- jid: JID,
+ jid: BareJID,
) -> Result<Vec<(Message, User)>, DatabaseError> {
Ok(logic.db().read_message_history_with_users(jid).await?)
}
pub async fn handle_delete_chat<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
- jid: JID,
+ jid: BareJID,
) -> Result<(), DatabaseError> {
Ok(logic.db().delete_chat(jid).await?)
}
@@ -88,7 +88,7 @@ pub async fn handle_delete_messaage<Fs: FileStore + Clone>(
pub async fn handle_get_user<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
- jid: JID,
+ jid: BareJID,
) -> Result<User, DatabaseError> {
Ok(logic.db().read_user(jid).await?)
}
diff --git a/filamento/src/logic/mod.rs b/filamento/src/logic/mod.rs
index 5e05dac..146f3b0 100644
--- a/filamento/src/logic/mod.rs
+++ b/filamento/src/logic/mod.rs
@@ -1,6 +1,6 @@
use std::{collections::HashMap, sync::Arc};
-use jid::JID;
+use jid::{BareJID, JID};
use lampada::{Connected, Logic, error::ReadError};
use stanza::client::Stanza;
use tokio::sync::{Mutex, mpsc, oneshot};
@@ -25,7 +25,7 @@ mod process_stanza;
#[derive(Clone)]
pub struct ClientLogic<Fs: FileStore> {
client: Client<Fs>,
- bare_jid: JID,
+ jid: BareJID,
db: Db,
pending: Pending,
update_sender: mpsc::Sender<UpdateMessage>,
@@ -80,7 +80,7 @@ impl Pending {
impl<Fs: FileStore> ClientLogic<Fs> {
pub fn new(
client: Client<Fs>,
- bare_jid: JID,
+ jid: BareJID,
db: Db,
update_sender: mpsc::Sender<UpdateMessage>,
file_store: Fs,
@@ -90,7 +90,7 @@ impl<Fs: FileStore> ClientLogic<Fs> {
pending: Pending::new(),
update_sender,
client,
- bare_jid,
+ jid,
file_store,
}
}
diff --git a/filamento/src/logic/offline.rs b/filamento/src/logic/offline.rs
index 606b04f..aa84f3d 100644
--- a/filamento/src/logic/offline.rs
+++ b/filamento/src/logic/offline.rs
@@ -1,6 +1,7 @@
use std::process::id;
use chrono::Utc;
+use jid::FullJID;
use lampada::error::WriteError;
use tracing::error;
use uuid::Uuid;
@@ -19,9 +20,13 @@ use crate::{
};
use super::{
+ ClientLogic,
local_only::{
- handle_delete_chat, handle_delete_messaage, handle_get_chat, handle_get_chat_and_user, handle_get_chats, handle_get_chats_ordered, handle_get_chats_ordered_with_latest_messages, handle_get_chats_ordered_with_latest_messages_and_users, handle_get_message, handle_get_messages, handle_get_messages_with_users, handle_get_user
- }, ClientLogic
+ handle_delete_chat, handle_delete_messaage, handle_get_chat, handle_get_chat_and_user,
+ handle_get_chats, handle_get_chats_ordered, handle_get_chats_ordered_with_latest_messages,
+ handle_get_chats_ordered_with_latest_messages_and_users, handle_get_message,
+ handle_get_messages, handle_get_messages_with_users, handle_get_user,
+ },
};
pub async fn handle_offline<Fs: FileStore + Clone>(logic: ClientLogic<Fs>, command: Command<Fs>) {
@@ -153,7 +158,7 @@ pub async fn handle_offline_result<Fs: FileStore + Clone>(
let message = Message {
id,
- from: logic.bare_jid.clone(),
+ from: logic.jid.clone(),
// TODO: failure reason
delivery: Some(Delivery::Failed),
timestamp,
@@ -163,11 +168,15 @@ pub async fn handle_offline_result<Fs: FileStore + Clone>(
// TODO: mark these as potentially failed upon client launch
if let Err(e) = logic
.db()
+ // TODO: can create message without a resource....
.create_message_with_user_resource(
message.clone(),
jid.clone(),
// TODO: when message is queued and sent, the from must also be updated with the correct resource
- logic.bare_jid.clone(),
+ FullJID {
+ bare_jid: logic.jid.clone(),
+ resourcepart: "unsent".to_string(),
+ },
)
.await
{
@@ -177,12 +186,12 @@ pub async fn handle_offline_result<Fs: FileStore + Clone>(
.await;
}
- let from = match logic.db().read_user(logic.bare_jid.clone()).await {
+ let from = match logic.db().read_user(logic.jid.clone()).await {
Ok(u) => u,
Err(e) => {
error!("{}", e);
User {
- jid: logic.bare_jid.clone(),
+ jid: logic.jid.clone(),
nick: None,
avatar: None,
}
@@ -192,7 +201,7 @@ pub async fn handle_offline_result<Fs: FileStore + Clone>(
logic
.update_sender()
.send(crate::UpdateMessage::Message {
- to: jid.as_bare(),
+ to: jid,
message,
from,
})
diff --git a/filamento/src/logic/online.rs b/filamento/src/logic/online.rs
index 9814ff2..febd3e1 100644
--- a/filamento/src/logic/online.rs
+++ b/filamento/src/logic/online.rs
@@ -3,7 +3,7 @@ use std::{io::Cursor, time::Duration};
use base64::{prelude::BASE64_STANDARD, Engine};
use chrono::Utc;
use image::ImageReader;
-use jid::JID;
+use jid::{BareJID, JID};
use lampada::{Connected, WriteMessage, error::WriteError};
use sha1::{Digest, Sha1};
use stanza::{
@@ -41,7 +41,7 @@ pub async fn handle_get_roster<Fs: FileStore + Clone>(
) -> Result<Vec<Contact>, RosterError> {
let iq_id = Uuid::new_v4().to_string();
let stanza = Stanza::Iq(Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq_id.to_string(),
to: None,
r#type: IqType::Get,
@@ -101,7 +101,7 @@ pub async fn handle_get_roster_with_users<Fs: FileStore + Clone>(
) -> Result<Vec<(Contact, User)>, RosterError> {
let iq_id = Uuid::new_v4().to_string();
let stanza = Stanza::Iq(Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq_id.to_string(),
to: None,
r#type: IqType::Get,
@@ -162,11 +162,11 @@ pub async fn handle_get_roster_with_users<Fs: FileStore + Clone>(
pub async fn handle_add_contact<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
connection: Connected,
- jid: JID,
+ jid: BareJID,
) -> Result<(), RosterError> {
let iq_id = Uuid::new_v4().to_string();
let set_stanza = Stanza::Iq(Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq_id.clone(),
to: None,
r#type: IqType::Set,
@@ -220,9 +220,10 @@ pub async fn handle_add_contact<Fs: FileStore + Clone>(
pub async fn handle_buddy_request<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
connection: Connected,
- jid: JID,
+ jid: BareJID,
) -> Result<(), SubscribeError> {
- let client_user = logic.db.read_user(logic.bare_jid.clone()).await?;
+ let jid: JID = jid.into();
+ let client_user = logic.db.read_user(logic.jid.clone()).await?;
let nick = client_user.nick.map(|nick| Nick(nick));
let presence = Stanza::Presence(stanza::client::presence::Presence {
to: Some(jid.clone()),
@@ -243,13 +244,13 @@ pub async fn handle_buddy_request<Fs: FileStore + Clone>(
pub async fn handle_subscription_request<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
connection: Connected,
- jid: JID,
+ jid: BareJID,
) -> Result<(), SubscribeError> {
// TODO: i should probably have builders
- let client_user = logic.db.read_user(logic.bare_jid.clone()).await?;
+ let client_user = logic.db.read_user(logic.jid.clone()).await?;
let nick = client_user.nick.map(|nick| Nick(nick));
let presence = Stanza::Presence(stanza::client::presence::Presence {
- to: Some(jid),
+ to: Some(jid.into()),
r#type: Some(stanza::client::presence::PresenceType::Subscribe),
nick,
..Default::default()
@@ -261,15 +262,16 @@ pub async fn handle_subscription_request<Fs: FileStore + Clone>(
pub async fn handle_accept_buddy_request<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
connection: Connected,
- jid: JID,
+ jid: BareJID,
) -> Result<(), SubscribeError> {
+ let jid: JID = jid.into();
let presence = Stanza::Presence(stanza::client::presence::Presence {
to: Some(jid.clone()),
r#type: Some(stanza::client::presence::PresenceType::Subscribed),
..Default::default()
});
connection.write_handle().write(presence).await?;
- let client_user = logic.db.read_user(logic.bare_jid.clone()).await?;
+ let client_user = logic.db.read_user(logic.jid.clone()).await?;
let nick = client_user.nick.map(|nick| Nick(nick));
let presence = Stanza::Presence(stanza::client::presence::Presence {
to: Some(jid),
@@ -284,12 +286,12 @@ pub async fn handle_accept_buddy_request<Fs: FileStore + Clone>(
pub async fn handle_accept_subscription_request<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
connection: Connected,
- jid: JID,
+ jid: BareJID,
) -> Result<(), SubscribeError> {
- let client_user = logic.db.read_user(logic.bare_jid.clone()).await?;
+ let client_user = logic.db.read_user(logic.jid.clone()).await?;
let nick = client_user.nick.map(|nick| Nick(nick));
let presence = Stanza::Presence(stanza::client::presence::Presence {
- to: Some(jid),
+ to: Some(jid.into()),
r#type: Some(stanza::client::presence::PresenceType::Subscribe),
nick,
..Default::default()
@@ -300,10 +302,10 @@ pub async fn handle_accept_subscription_request<Fs: FileStore + Clone>(
pub async fn handle_unsubscribe_from_contact(
connection: Connected,
- jid: JID,
+ jid: BareJID,
) -> Result<(), WriteError> {
let presence = Stanza::Presence(stanza::client::presence::Presence {
- to: Some(jid),
+ to: Some(jid.into()),
r#type: Some(stanza::client::presence::PresenceType::Unsubscribe),
..Default::default()
});
@@ -311,9 +313,9 @@ pub async fn handle_unsubscribe_from_contact(
Ok(())
}
-pub async fn handle_unsubscribe_contact(connection: Connected, jid: JID) -> Result<(), WriteError> {
+pub async fn handle_unsubscribe_contact(connection: Connected, jid: BareJID) -> Result<(), WriteError> {
let presence = Stanza::Presence(stanza::client::presence::Presence {
- to: Some(jid),
+ to: Some(jid.into()),
r#type: Some(stanza::client::presence::PresenceType::Unsubscribed),
..Default::default()
});
@@ -321,7 +323,8 @@ pub async fn handle_unsubscribe_contact(connection: Connected, jid: JID) -> Resu
Ok(())
}
-pub async fn handle_unfriend_contact(connection: Connected, jid: JID) -> Result<(), WriteError> {
+pub async fn handle_unfriend_contact(connection: Connected, jid: BareJID) -> Result<(), WriteError> {
+ let jid: JID = jid.into();
let presence = Stanza::Presence(stanza::client::presence::Presence {
to: Some(jid.clone()),
r#type: Some(stanza::client::presence::PresenceType::Unsubscribe),
@@ -340,11 +343,11 @@ pub async fn handle_unfriend_contact(connection: Connected, jid: JID) -> Result<
pub async fn handle_delete_contact<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
connection: Connected,
- jid: JID,
+ jid: BareJID,
) -> Result<(), RosterError> {
let iq_id = Uuid::new_v4().to_string();
let set_stanza = Stanza::Iq(Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq_id.clone(),
to: None,
r#type: IqType::Set,
@@ -399,7 +402,7 @@ pub async fn handle_delete_contact<Fs: FileStore + Clone>(
pub async fn handle_update_contact<Fs: FileStore + Clone>(
logic: &ClientLogic<Fs>,
connection: Connected,
- jid: JID,
+ jid: BareJID,
contact_update: ContactUpdate,
) -> Result<(), RosterError> {
let iq_id = Uuid::new_v4().to_string();
@@ -410,7 +413,8 @@ pub async fn handle_update_contact<Fs: FileStore + Clone>(
.map(|group| stanza::roster::Group(Some(group))),
);
let set_stanza = Stanza::Iq(Iq {
- from: Some(connection.jid().clone()),
+ // TODO: these clones could technically be avoided?
+ from: Some(connection.jid().clone().into()),
id: iq_id.clone(),
to: None,
r#type: IqType::Set,
@@ -474,7 +478,7 @@ pub async fn handle_set_status<Fs: FileStore + Clone>(
Ok(())
}
-pub async fn handle_send_message<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>, connection: Connected, jid: JID, body: Body) {
+pub async fn handle_send_message<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>, connection: Connected, jid: BareJID, body: Body) {
// upsert the chat and user the message will be delivered to. if there is a conflict, it will return whatever was there, otherwise it will return false by default.
// let have_chatted = logic.db().upsert_chat_and_user(&jid).await.unwrap_or(false);
let have_chatted = match logic.db().upsert_chat_and_user(jid.clone()).await {
@@ -490,7 +494,7 @@ pub async fn handle_send_message<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>,
let nick;
let mark_chat_as_chatted;
if have_chatted == false {
- match logic.db.read_user(logic.bare_jid.clone()).await {
+ match logic.db.read_user(logic.jid.clone()).await {
Ok(u) => {
nick = u.nick.map(|nick| Nick(nick));
mark_chat_as_chatted = true;
@@ -513,7 +517,7 @@ pub async fn handle_send_message<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>,
let timestamp = Utc::now();
let message = Message {
id,
- from: connection.jid().as_bare(),
+ from: connection.jid().to_bare(),
body: body.clone(),
timestamp,
delivery: Some(Delivery::Sending),
@@ -532,12 +536,12 @@ pub async fn handle_send_message<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>,
.await;
}
- let from = match logic.db().read_user(logic.bare_jid.clone()).await {
+ let from = match logic.db().read_user(logic.jid.clone()).await {
Ok(u) => u,
Err(e) => {
error!("{}", e);
User {
- jid: logic.bare_jid.clone(),
+ jid: logic.jid.clone(),
nick: None,
avatar: None,
}
@@ -548,7 +552,7 @@ pub async fn handle_send_message<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>,
logic
.update_sender()
.send(UpdateMessage::Message {
- to: jid.as_bare(),
+ to: jid.clone(),
message,
from,
})
@@ -556,9 +560,9 @@ pub async fn handle_send_message<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>,
// prepare the message stanza
let message_stanza = Stanza::Message(stanza::client::message::Message {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: Some(id.to_string()),
- to: Some(jid.clone()),
+ to: Some(jid.clone().into()),
// TODO: specify message type
r#type: stanza::client::message::MessageType::Chat,
// TODO: lang ?
@@ -639,7 +643,7 @@ pub async fn handle_disco_info<Fs: FileStore + Clone>(
) -> Result<Info, DiscoError> {
let id = Uuid::new_v4().to_string();
let request = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: id.clone(),
to: jid.clone(),
r#type: IqType::Get,
@@ -667,7 +671,7 @@ pub async fn handle_disco_info<Fs: FileStore + Clone>(
}) if r#type == IqType::Result || r#type == IqType::Error => {
if from == jid || {
if jid == None {
- from == Some(connection.jid().as_bare())
+ from == Some(connection.jid().to_bare().into())
} else {
false
}
@@ -694,7 +698,7 @@ pub async fn handle_disco_info<Fs: FileStore + Clone>(
}
} else {
Err(DiscoError::IncorrectEntity(
- from.unwrap_or_else(|| connection.jid().as_bare()),
+ from.unwrap_or_else(|| connection.jid().to_bare().into()),
))
}
}
@@ -710,7 +714,7 @@ pub async fn handle_disco_items<Fs: FileStore + Clone>(
) -> Result<Items, DiscoError> {
let id = Uuid::new_v4().to_string();
let request = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: id.clone(),
to: jid.clone(),
r#type: IqType::Get,
@@ -736,7 +740,7 @@ pub async fn handle_disco_items<Fs: FileStore + Clone>(
}) if r#type == IqType::Result || r#type == IqType::Error => {
if from == jid || {
if jid == None {
- from == Some(connection.jid().as_bare())
+ from == Some(connection.jid().to_bare().into())
} else {
false
}
@@ -763,7 +767,7 @@ pub async fn handle_disco_items<Fs: FileStore + Clone>(
}
} else {
Err(DiscoError::IncorrectEntity(
- from.unwrap_or_else(|| connection.jid().as_bare()),
+ from.unwrap_or_else(|| connection.jid().to_bare().into()),
))
}
}
@@ -828,7 +832,7 @@ pub async fn handle_publish_pep_item<Fs: FileStore + Clone>(
},
};
let request = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: id.clone(),
to: None,
r#type: IqType::Set,
@@ -850,7 +854,7 @@ pub async fn handle_publish_pep_item<Fs: FileStore + Clone>(
// 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 == None ||
- from == Some(connection.jid().as_bare())
+ from == Some(connection.jid().to_bare().into())
{
match r#type {
IqType::Result => {
@@ -870,7 +874,7 @@ pub async fn handle_publish_pep_item<Fs: FileStore + Clone>(
}
} else {
Err(PEPError::IncorrectEntity(
- from.unwrap_or_else(|| connection.jid().as_bare()),
+ from.unwrap_or_else(|| connection.jid().to_bare().into()),
))
}
}
@@ -878,10 +882,11 @@ pub async fn handle_publish_pep_item<Fs: FileStore + Clone>(
}
}
-pub async fn handle_get_pep_item<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>, connection: Connected, jid: Option<JID>, node: String, id: String) -> Result<pep::Item, PEPError> {
+pub async fn handle_get_pep_item<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>, connection: Connected, jid: Option<BareJID>, node: String, id: String) -> Result<pep::Item, PEPError> {
+ let jid = jid.map(|jid| Into::<JID>::into(jid));
let stanza_id = Uuid::new_v4().to_string();
let request = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: stanza_id.clone(),
to: jid.clone(),
r#type: IqType::Get,
@@ -909,7 +914,7 @@ pub async fn handle_get_pep_item<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>,
}) if r#type == IqType::Result || r#type == IqType::Error => {
if from == jid || {
if jid == None {
- from == Some(connection.jid().as_bare())
+ from == Some(connection.jid().to_bare().into())
} else {
false
}
@@ -955,7 +960,7 @@ pub async fn handle_get_pep_item<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>,
} else {
// TODO: include expected entity
Err(PEPError::IncorrectEntity(
- from.unwrap_or_else(|| connection.jid().as_bare()),
+ from.unwrap_or_else(|| connection.jid().to_bare().into()),
))
}
}
@@ -1024,7 +1029,7 @@ pub async fn handle_delete_pep_node<Fs: FileStore + Clone>(
) -> Result<(), PEPError> {
let id = Uuid::new_v4().to_string();
let request = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: id.clone(),
to: None,
r#type: IqType::Set,
@@ -1046,7 +1051,7 @@ pub async fn handle_delete_pep_node<Fs: FileStore + Clone>(
// 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 == None ||
- from == Some(connection.jid().as_bare())
+ from == Some(connection.jid().to_bare().into())
{
match r#type {
IqType::Result => {
@@ -1067,7 +1072,7 @@ pub async fn handle_delete_pep_node<Fs: FileStore + Clone>(
}
} else {
Err(PEPError::IncorrectEntity(
- from.unwrap_or_else(|| connection.jid().as_bare()),
+ from.unwrap_or_else(|| connection.jid().to_bare().into()),
))
}
}
diff --git a/filamento/src/logic/process_stanza.rs b/filamento/src/logic/process_stanza.rs
index 30d0830..67b0d3f 100644
--- a/filamento/src/logic/process_stanza.rs
+++ b/filamento/src/logic/process_stanza.rs
@@ -70,7 +70,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
// TODO: proper id xep
.map(|id| Uuid::from_str(&id).unwrap_or_else(|_| Uuid::new_v4()))
.unwrap_or_else(|| Uuid::new_v4()),
- from: from.as_bare(),
+ from: from.to_bare(),
timestamp,
body: Body {
body: body.body.unwrap_or_default(),
@@ -80,31 +80,34 @@ pub async fn recv_message<Fs: FileStore + Clone>(
// TODO: process message type="error"
// save the message to the database
- match logic.db().upsert_chat_and_user(from.clone()).await {
- Ok(_) => {
- if let Err(e) = logic
- .db()
- .create_message_with_user_resource(
- message.clone(),
- from.clone(),
- from.clone(),
- )
- .await
- {
- error!("failed to create message: {}", e);
+ match logic.db().upsert_chat_and_user(from.to_bare()).await {
+ Ok(_) => match from.as_full() {
+ Ok(from) => {
+ if let Err(e) = logic
+ .db()
+ .create_message_with_user_resource(
+ message.clone(),
+ from.to_bare(),
+ from.clone(),
+ )
+ .await
+ {
+ error!("failed to create message: {}", e);
+ }
}
- }
+ Err(e) => error!("failed to create message: {}", e),
+ },
Err(e) => {
error!("failed to upsert chat and user: {}", e);
}
};
- let from_user = match logic.db().read_user(from.as_bare()).await {
+ let from_user = match logic.db().read_user(from.to_bare()).await {
Ok(u) => u,
Err(e) => {
error!("{}", e);
User {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: None,
avatar: None,
}
@@ -115,7 +118,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
logic
.update_sender()
.send(UpdateMessage::Message {
- to: from.as_bare(),
+ to: from.to_bare(),
from: from_user,
message,
})
@@ -125,13 +128,13 @@ pub async fn recv_message<Fs: FileStore + Clone>(
if let Some(nick) = stanza_message.nick {
let nick = nick.0;
if nick.is_empty() {
- match logic.db().delete_user_nick(from.as_bare()).await {
+ match logic.db().delete_user_nick(from.to_bare()).await {
Ok(changed) => {
if changed {
logic
.update_sender()
.send(UpdateMessage::NickChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: None,
})
.await;
@@ -145,7 +148,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
logic
.update_sender()
.send(UpdateMessage::NickChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: None,
})
.await;
@@ -154,7 +157,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
} else {
match logic
.db()
- .upsert_user_nick(from.as_bare(), nick.clone())
+ .upsert_user_nick(from.to_bare(), nick.clone())
.await
{
Ok(changed) => {
@@ -162,7 +165,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
logic
.update_sender()
.send(UpdateMessage::NickChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: Some(nick),
})
.await;
@@ -176,7 +179,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
logic
.update_sender()
.send(UpdateMessage::NickChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: Some(nick),
})
.await;
@@ -199,7 +202,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
if nick.is_empty() {
match logic
.db()
- .delete_user_nick(from.as_bare())
+ .delete_user_nick(from.to_bare())
.await
{
Ok(changed) => {
@@ -207,7 +210,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
logic
.update_sender()
.send(UpdateMessage::NickChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: None,
})
.await;
@@ -223,7 +226,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
logic
.update_sender()
.send(UpdateMessage::NickChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: None,
})
.await;
@@ -233,7 +236,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
match logic
.db()
.upsert_user_nick(
- from.as_bare(),
+ from.to_bare(),
nick.clone(),
)
.await
@@ -243,7 +246,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
logic
.update_sender()
.send(UpdateMessage::NickChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: Some(nick),
})
.await;
@@ -259,7 +262,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
logic
.update_sender()
.send(UpdateMessage::NickChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
nick: Some(nick),
})
.await;
@@ -294,7 +297,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
match logic
.db()
.upsert_user_avatar(
- from.as_bare(),
+ from.to_bare(),
metadata.id.clone(),
)
.await
@@ -323,7 +326,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
}) {
Ok(false) => {
// get data
- let pep_item = logic.client().get_pep_item(Some(from.as_bare()), "urn:xmpp:avatar:data".to_string(), metadata.id.clone()).await.map_err(|err| Into::<AvatarUpdateError<Fs>>::into(err))?;
+ let pep_item = logic.client().get_pep_item(Some(from.to_bare()), "urn:xmpp:avatar:data".to_string(), metadata.id.clone()).await.map_err(|err| Into::<AvatarUpdateError<Fs>>::into(err))?;
match pep_item {
crate::pep::Item::AvatarData(data) => {
let data = data.map(|data| data.data_b64).unwrap_or_default().replace("\n", "");
@@ -344,7 +347,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
.update_sender()
.send(
UpdateMessage::AvatarChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
id: Some(
metadata.id.clone(),
),
@@ -371,7 +374,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
.update_sender()
.send(
UpdateMessage::AvatarChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
id: Some(
metadata.id.clone(),
),
@@ -401,7 +404,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
// delete avatar
match logic
.db()
- .delete_user_avatar(from.as_bare())
+ .delete_user_avatar(from.to_bare())
.await
{
Ok((changed, old_avatar)) => {
@@ -419,7 +422,7 @@ pub async fn recv_message<Fs: FileStore + Clone>(
.update_sender()
.send(
UpdateMessage::AvatarChanged {
- jid: from.as_bare(),
+ jid: from.to_bare(),
id: None,
},
)
@@ -488,6 +491,7 @@ pub async fn recv_presence(
}
stanza::client::presence::PresenceType::Subscribe => {
// may get a subscription request from somebody who is not a contact!!! therefore should be its own kind of event
+ let from = from.try_into()?;
Ok(Some(UpdateMessage::SubscriptionRequest(from)))
}
stanza::client::presence::PresenceType::Unavailable => {
@@ -546,7 +550,8 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
iq: Iq,
) -> Result<Option<UpdateMessage>, IqProcessError> {
if let Some(to) = &iq.to {
- if *to == *connection.jid() {
+ // TODO: this clone could mayb b avoided
+ if *to == connection.jid().clone().into() {
} else {
return Err(IqProcessError::Iq(IqError::IncorrectAddressee(to.clone())));
}
@@ -556,7 +561,9 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
let from = iq
.from
.clone()
- .unwrap_or_else(|| connection.server().clone());
+ // TODO: maybe actually store the server in the connection again LOL
+ // .unwrap_or_else(|| connection.server().clone());
+ .unwrap_or_else(|| connection.jid().domainpart.parse().unwrap());
let id = iq.id.clone();
debug!("received iq result with id `{}` from {}", id, from);
logic
@@ -570,7 +577,8 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
let from = iq
.from
.clone()
- .unwrap_or_else(|| connection.server().clone());
+ // .unwrap_or_else(|| connection.server().clone());
+ .unwrap_or_else(|| connection.jid().domainpart.parse().unwrap());
if let Some(query) = iq.query {
match query {
stanza::client::iq::Query::DiscoInfo(query) => {
@@ -594,7 +602,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
}
Err(_e) => {
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Error,
@@ -614,7 +622,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
},
Err(_e) => {
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Error,
@@ -634,7 +642,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
}
};
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Result,
@@ -653,7 +661,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
_ => {
warn!("received unsupported iq get from {}", from);
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Error,
@@ -677,7 +685,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
} else {
info!("received malformed iq query from {}", from);
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Error,
@@ -698,7 +706,8 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
let from = iq
.from
.clone()
- .unwrap_or_else(|| connection.server().clone());
+ // .unwrap_or_else(|| connection.server().clone());
+ .unwrap_or_else(|| connection.jid().domainpart.parse().unwrap());
if let Some(query) = iq.query {
match query {
stanza::client::iq::Query::Roster(mut query) => {
@@ -725,7 +734,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
.await;
}
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Result,
@@ -751,7 +760,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
} else {
warn!("received malformed roster push");
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Error,
@@ -771,7 +780,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
_ => {
warn!("received unsupported iq set from {}", from);
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Error,
@@ -791,7 +800,7 @@ pub async fn recv_iq<Fs: FileStore + Clone>(
} else {
warn!("received malformed iq set from {}", from);
let iq = Iq {
- from: Some(connection.jid().clone()),
+ from: Some(connection.jid().clone().into()),
id: iq.id,
to: iq.from,
r#type: IqType::Error,
@@ -820,7 +829,7 @@ pub async fn process_stanza<Fs: FileStore + Clone>(
Stanza::Presence(presence) => Ok(recv_presence(presence).await?),
Stanza::Iq(iq) => Ok(recv_iq(logic, connection.clone(), iq).await?),
// unreachable, always caught by lampada
- // TODO: make cleaner than this in some way
+ // TODO: make cleaner than this in some way, by just reexporting a stanza enum from lampada ig.
Stanza::Error(error) => {
unreachable!()
}
diff --git a/filamento/src/roster.rs b/filamento/src/roster.rs
index 6b61e10..0498278 100644
--- a/filamento/src/roster.rs
+++ b/filamento/src/roster.rs
@@ -1,6 +1,6 @@
use std::{collections::HashSet, fmt::Display};
-use jid::JID;
+use jid::BareJID;
use rusqlite::{
ToSql,
types::{FromSql, ToSqlOutput, Value},
@@ -15,7 +15,7 @@ pub struct ContactUpdate {
#[cfg_attr(feature = "reactive_stores", derive(reactive_stores::Store))]
pub struct Contact {
// jid is the id used to reference everything, but not the primary key
- pub user_jid: JID,
+ pub user_jid: BareJID,
pub subscription: Subscription,
/// client user defined name
pub name: Option<String>,
diff --git a/filamento/src/user.rs b/filamento/src/user.rs
index f30933c..f962a4c 100644
--- a/filamento/src/user.rs
+++ b/filamento/src/user.rs
@@ -1,10 +1,10 @@
-use jid::JID;
+use jid::BareJID;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "reactive_stores", derive(reactive_stores::Store))]
pub struct User {
- pub jid: JID,
+ pub jid: BareJID,
pub nick: Option<String>,
pub avatar: Option<String>,
// pub cached_status_message: Option<String>,
diff --git a/jid/src/lib.rs b/jid/src/lib.rs
index 47ca497..3b40094 100644
--- a/jid/src/lib.rs
+++ b/jid/src/lib.rs
@@ -1,15 +1,53 @@
-use std::{borrow::Cow, error::Error, fmt::Display, str::FromStr};
+use std::{borrow::Cow, error::Error, fmt::Display, ops::Deref, str::FromStr};
// #[cfg(feature = "sqlx")]
// use sqlx::Sqlite;
-#[derive(PartialEq, Debug, Clone, Eq, Hash)]
+#[derive(PartialEq, Debug, Clone, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-pub struct JID {
- // TODO: validate localpart (length, char]
- pub localpart: Option<String>,
- pub domainpart: String,
- pub resourcepart: Option<String>,
+pub enum JID {
+ Full(FullJID),
+ Bare(BareJID),
+}
+
+impl JID {
+ pub fn resourcepart(&self) -> Option<&String> {
+ match self {
+ JID::Full(full_jid) => Some(&full_jid.resourcepart),
+ JID::Bare(_bare_jid) => None,
+ }
+ }
+}
+
+impl From<FullJID> for JID {
+ fn from(value: FullJID) -> Self {
+ Self::Full(value)
+ }
+}
+
+impl From<BareJID> for JID {
+ fn from(value: BareJID) -> Self {
+ Self::Bare(value)
+ }
+}
+
+impl Deref for JID {
+ type Target = BareJID;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ JID::Full(full_jid) => full_jid.as_bare(),
+ JID::Bare(bare_jid) => bare_jid,
+ }
+ }
+}
+
+impl Deref for FullJID {
+ type Target = BareJID;
+
+ fn deref(&self) -> &Self::Target {
+ &self.bare_jid
+ }
}
impl<'a> Into<Cow<'a, str>> for &'a JID {
@@ -21,15 +59,45 @@ impl<'a> Into<Cow<'a, str>> for &'a JID {
impl Display for JID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ JID::Full(full_jid) => full_jid.fmt(f),
+ JID::Bare(bare_jid) => bare_jid.fmt(f),
+ }
+ }
+}
+
+#[derive(PartialEq, Debug, Clone, Eq, Hash, PartialOrd, Ord)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct FullJID {
+ pub bare_jid: BareJID,
+ pub resourcepart: String,
+}
+
+impl Display for FullJID {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.bare_jid.fmt(f)?;
+ f.write_str("/")?;
+ f.write_str(&self.resourcepart)?;
+ Ok(())
+ }
+}
+
+#[derive(PartialEq, Debug, Clone, Eq, Hash, PartialOrd, Ord)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct BareJID {
+ // TODO: validate and don't have public fields
+ // TODO: validate localpart (length, char]
+ pub localpart: Option<String>,
+ pub domainpart: String,
+}
+
+impl Display for BareJID {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(localpart) = &self.localpart {
f.write_str(localpart)?;
f.write_str("@")?;
}
f.write_str(&self.domainpart)?;
- if let Some(resourcepart) = &self.resourcepart {
- f.write_str("/")?;
- f.write_str(resourcepart)?;
- }
Ok(())
}
}
@@ -51,52 +119,65 @@ impl rusqlite::types::FromSql for JID {
}
#[cfg(feature = "rusqlite")]
-impl From<ParseError> for rusqlite::types::FromSqlError {
- fn from(value: ParseError) -> Self {
- Self::Other(Box::new(value))
+impl rusqlite::ToSql for FullJID {
+ fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
+ Ok(rusqlite::types::ToSqlOutput::Owned(
+ rusqlite::types::Value::Text(self.to_string()),
+ ))
}
}
-// #[cfg(feature = "sqlx")]
-// impl sqlx::Type<Sqlite> for JID {
-// fn type_info() -> <Sqlite as sqlx::Database>::TypeInfo {
-// <&str as sqlx::Type<Sqlite>>::type_info()
-// }
-// }
+#[cfg(feature = "rusqlite")]
+impl rusqlite::types::FromSql for FullJID {
+ fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
+ Ok(JID::from_str(value.as_str()?)?.try_into()?)
+ }
+}
-// #[cfg(feature = "sqlx")]
-// impl sqlx::Decode<'_, Sqlite> for JID {
-// fn decode(
-// value: <Sqlite as sqlx::Database>::ValueRef<'_>,
-// ) -> Result<Self, sqlx::error::BoxDynError> {
-// let value = <&str as sqlx::Decode<Sqlite>>::decode(value)?;
+#[cfg(feature = "rusqlite")]
+impl rusqlite::ToSql for BareJID {
+ fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
+ Ok(rusqlite::types::ToSqlOutput::Owned(
+ rusqlite::types::Value::Text(self.to_string()),
+ ))
+ }
+}
-// Ok(value.parse()?)
-// }
-// }
+#[cfg(feature = "rusqlite")]
+impl rusqlite::types::FromSql for BareJID {
+ fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
+ Ok(JID::from_str(value.as_str()?)?.try_into()?)
+ }
+}
-// #[cfg(feature = "sqlx")]
-// impl sqlx::Encode<'_, Sqlite> for JID {
-// fn encode_by_ref(
-// &self,
-// buf: &mut <Sqlite as sqlx::Database>::ArgumentBuffer<'_>,
-// ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
-// let jid = self.to_string();
-// <String as sqlx::Encode<Sqlite>>::encode(jid, buf)
-// }
-// }
+#[cfg(feature = "rusqlite")]
+impl From<ParseError> for rusqlite::types::FromSqlError {
+ fn from(value: ParseError) -> Self {
+ Self::Other(Box::new(value))
+ }
+}
+
+#[cfg(feature = "rusqlite")]
+impl From<JIDError> for rusqlite::types::FromSqlError {
+ fn from(value: JIDError) -> Self {
+ Self::Other(Box::new(value))
+ }
+}
#[derive(Debug, Clone)]
pub enum JIDError {
NoResourcePart,
ParseError(ParseError),
+ ContainsResourcepart,
}
impl Display for JIDError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
+ // TODO: separate jid errors?
JIDError::NoResourcePart => f.write_str("resourcepart missing"),
JIDError::ParseError(parse_error) => parse_error.fmt(f),
+ JIDError::ContainsResourcepart => f.write_str("contains resourcepart"),
}
}
}
@@ -122,36 +203,105 @@ impl Display for ParseError {
impl Error for ParseError {}
+impl FullJID {
+ pub fn new(localpart: Option<String>, domainpart: String, resourcepart: String) -> Self {
+ Self {
+ bare_jid: BareJID::new(localpart, domainpart),
+ resourcepart,
+ }
+ }
+
+ pub fn as_bare(&self) -> &BareJID {
+ &self.bare_jid
+ }
+
+ pub fn to_bare(&self) -> BareJID {
+ self.bare_jid.clone()
+ }
+}
+
+impl BareJID {
+ pub fn new(localpart: Option<String>, domainpart: String) -> Self {
+ Self {
+ localpart,
+ domainpart,
+ }
+ }
+}
+
+impl TryFrom<JID> for BareJID {
+ type Error = JIDError;
+
+ fn try_from(value: JID) -> Result<Self, Self::Error> {
+ match value {
+ JID::Full(_full_jid) => Err(JIDError::ContainsResourcepart),
+ JID::Bare(bare_jid) => Ok(bare_jid),
+ }
+ }
+}
+
impl JID {
pub fn new(
localpart: Option<String>,
domainpart: String,
resourcepart: Option<String>,
) -> Self {
- Self {
- localpart,
- domainpart: domainpart.parse().unwrap(),
- resourcepart,
+ if let Some(resourcepart) = resourcepart {
+ Self::Full(FullJID::new(localpart, domainpart, resourcepart))
+ } else {
+ Self::Bare(BareJID::new(localpart, domainpart))
}
}
- pub fn as_bare(&self) -> Self {
- Self {
- localpart: self.localpart.clone(),
- domainpart: self.domainpart.clone(),
- resourcepart: None,
+ pub fn as_bare(&self) -> &BareJID {
+ match self {
+ JID::Full(full_jid) => full_jid.as_bare(),
+ JID::Bare(bare_jid) => &bare_jid,
}
}
- pub fn as_full(&self) -> Result<&Self, JIDError> {
- if let Some(_) = self.resourcepart {
- Ok(&self)
- } else {
- Err(JIDError::NoResourcePart)
+ pub fn to_bare(&self) -> BareJID {
+ match self {
+ JID::Full(full_jid) => full_jid.to_bare(),
+ JID::Bare(bare_jid) => bare_jid.clone(),
+ }
+ }
+
+ pub fn as_full(&self) -> Result<&FullJID, JIDError> {
+ match self {
+ JID::Full(full_jid) => Ok(full_jid),
+ JID::Bare(_bare_jid) => Err(JIDError::NoResourcePart),
+ }
+ }
+}
+
+impl TryFrom<JID> for FullJID {
+ type Error = JIDError;
+
+ fn try_from(value: JID) -> Result<Self, Self::Error> {
+ match value {
+ JID::Full(full_jid) => Ok(full_jid),
+ JID::Bare(_bare_jid) => Err(JIDError::NoResourcePart),
}
}
}
+impl FromStr for BareJID {
+ type Err = JIDError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(JID::from_str(s)?.try_into()?)
+ }
+}
+
+impl FromStr for FullJID {
+ type Err = JIDError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(JID::from_str(s)?.try_into()?)
+ }
+}
+
impl FromStr for JID {
type Err = ParseError;
@@ -192,6 +342,12 @@ impl FromStr for JID {
}
}
+impl From<ParseError> for JIDError {
+ fn from(value: ParseError) -> Self {
+ JIDError::ParseError(value)
+ }
+}
+
impl TryFrom<String> for JID {
type Error = ParseError;
@@ -256,3 +412,32 @@ mod tests {
)
}
}
+
+// #[cfg(feature = "sqlx")]
+// impl sqlx::Type<Sqlite> for JID {
+// fn type_info() -> <Sqlite as sqlx::Database>::TypeInfo {
+// <&str as sqlx::Type<Sqlite>>::type_info()
+// }
+// }
+
+// #[cfg(feature = "sqlx")]
+// impl sqlx::Decode<'_, Sqlite> for JID {
+// fn decode(
+// value: <Sqlite as sqlx::Database>::ValueRef<'_>,
+// ) -> Result<Self, sqlx::error::BoxDynError> {
+// let value = <&str as sqlx::Decode<Sqlite>>::decode(value)?;
+
+// Ok(value.parse()?)
+// }
+// }
+
+// #[cfg(feature = "sqlx")]
+// impl sqlx::Encode<'_, Sqlite> for JID {
+// fn encode_by_ref(
+// &self,
+// buf: &mut <Sqlite as sqlx::Database>::ArgumentBuffer<'_>,
+// ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
+// let jid = self.to_string();
+// <String as sqlx::Encode<Sqlite>>::encode(jid, buf)
+// }
+// }
diff --git a/lampada/src/connection/mod.rs b/lampada/src/connection/mod.rs
index 3a3187f..51c3758 100644
--- a/lampada/src/connection/mod.rs
+++ b/lampada/src/connection/mod.rs
@@ -7,7 +7,7 @@ use std::{
time::Duration,
};
-use jid::JID;
+use jid::{BareJID, FullJID, JID};
use luz::jabber_stream::bound_stream::BoundJabberStream;
use read::{ReadControl, ReadControlHandle, ReadState};
use stanza::{client::Stanza, stream_error::Error as StreamError};
@@ -121,12 +121,11 @@ where
break
}
- let mut jid = self.connected.jid.clone();
- let mut domain = jid.domainpart.clone();
+ let mut jid = self.connected.jid.clone().into();
// TODO: make sure connect_and_login does not modify the jid, but instead returns a jid. or something like that
- let connection = luz::connect_and_login(&mut jid, &*self.password, &mut domain).await;
+ let connection = luz::connect_and_login(&jid, &*self.password).await;
match connection {
- Ok(c) => {
+ Ok((c, full_jid)) => {
let (read, write) = c.split();
let (send, recv) = oneshot::channel();
self.writer_crash = recv;
@@ -169,12 +168,11 @@ where
else => break,
};
- let mut jid = self.connected.jid.clone();
- let mut domain = jid.domainpart.clone();
+ let mut jid = self.connected.jid.clone().into();
// TODO: same here
- let connection = luz::connect_and_login(&mut jid, &*self.password, &mut domain).await;
+ let connection = luz::connect_and_login(&jid, &*self.password).await;
match connection {
- Ok(c) => {
+ Ok((c, full_jid)) => {
let (read, write) = c.split();
let (send, recv) = oneshot::channel();
self.writer_crash = recv;
@@ -214,11 +212,10 @@ where
else => break,
};
- let mut jid = self.connected.jid.clone();
- let mut domain = jid.domainpart.clone();
- let connection = luz::connect_and_login(&mut jid, &*self.password, &mut domain).await;
+ let mut jid = self.connected.jid.clone().into();
+ let connection = luz::connect_and_login(&jid, &*self.password).await;
match connection {
- Ok(c) => {
+ Ok((c, full_jid)) => {
let (read, write) = c.split();
let (send, recv) = oneshot::channel();
self.writer_crash = recv;
@@ -305,8 +302,7 @@ impl SupervisorHandle {
pub fn new<Lgc>(
streams: BoundJabberStream,
on_crash: oneshot::Sender<()>,
- jid: JID,
- server: JID,
+ jid: FullJID,
password: Arc<String>,
logic: Lgc,
) -> (WriteHandle, Self)
@@ -327,7 +323,6 @@ impl SupervisorHandle {
let connected = Connected {
jid,
write_handle: write_handle.clone(),
- server,
};
let supervisor_sender = SupervisorSender {
diff --git a/lampada/src/error.rs b/lampada/src/error.rs
index f29d0cc..a5af3a2 100644
--- a/lampada/src/error.rs
+++ b/lampada/src/error.rs
@@ -24,7 +24,7 @@ pub enum ConnectionError {
#[error("disconnected")]
Disconnected,
#[error("invalid server jid: {0}")]
- InvalidServerJID(#[from] jid::ParseError),
+ InvalidServerJID(#[from] jid::JIDError),
}
#[derive(Debug, Error, Clone)]
diff --git a/lampada/src/lib.rs b/lampada/src/lib.rs
index 6b6cbe8..d1d451e 100644
--- a/lampada/src/lib.rs
+++ b/lampada/src/lib.rs
@@ -12,6 +12,7 @@ pub use connection::write::WriteMessage;
pub use connection::SupervisorSender;
use error::ConnectionError;
use futures::{future::Fuse, FutureExt};
+use jid::{BareJID, FullJID};
use luz::JID;
use stanza::client::{
iq::{self, Iq, IqType},
@@ -36,24 +37,18 @@ pub mod error;
#[derive(Clone)]
pub struct Connected {
// full jid will stay stable across reconnections
- jid: JID,
+ jid: FullJID,
write_handle: WriteHandle,
- // the server jid
- server: JID,
}
impl Connected {
- pub fn jid(&self) -> &JID {
+ pub fn jid(&self) -> &FullJID {
&self.jid
}
pub fn write_handle(&self) -> &WriteHandle {
&self.write_handle
}
-
- pub fn server(&self) -> &JID {
- &self.server
- }
}
/// everything that a particular xmpp client must implement
@@ -227,34 +222,17 @@ where
.await;
}
None => {
- let mut jid = self.jid.clone();
- let mut domain = jid.domainpart.clone();
// TODO: check what happens upon reconnection with same resource (this is probably what one wants to do and why jid should be mutated from a bare jid to one with a resource)
let streams_result =
- luz::connect_and_login(&mut jid, &*self.password, &mut domain)
- .await;
- let server: JID = match domain.parse() {
- Ok(j) => j,
- Err(e) => {
- self.logic
- .clone()
- .handle_connection_error(ConnectionError::InvalidServerJID(
- e.clone(),
- ))
- .await;
- sender.send(Err(ConnectionError::InvalidServerJID(e)));
- continue;
- }
- };
+ luz::connect_and_login(&self.jid, &*self.password).await;
match streams_result {
- Ok(s) => {
+ Ok((s, full_jid)) => {
debug!("ok stream result");
let (shutdown_send, shutdown_recv) = oneshot::channel::<()>();
let (writer, supervisor) = SupervisorHandle::new(
s,
shutdown_send,
- jid.clone(),
- server.clone(),
+ full_jid.clone(),
self.password.clone(),
self.logic.clone(),
);
@@ -262,18 +240,16 @@ where
let shutdown_recv = shutdown_recv.fuse();
self.connection_supervisor_shutdown = shutdown_recv;
- let resource = jid.resourcepart.clone().expect("client somehow connected without binding");
let connected = Connected {
- jid,
+ jid: full_jid.clone(),
write_handle: writer,
- server,
};
self.logic.clone().handle_connect(connected.clone()).await;
self.connected = Some((connected, supervisor));
// REMEMBER TO NOTIFY IT@S GOOD
- sender.send(Ok(resource));
+ sender.send(Ok(full_jid.resourcepart));
}
Err(e) => {
tracing::error!("error: {}", e);
diff --git a/luz/src/client/tcp.rs b/luz/src/client/tcp.rs
index 4e35ef0..a30eccc 100644
--- a/luz/src/client/tcp.rs
+++ b/luz/src/client/tcp.rs
@@ -1,3 +1,4 @@
+use jid::FullJID;
use rsasl::config::SASLConfig;
use stanza::{
sasl::Mechanisms,
@@ -10,22 +11,25 @@ use crate::{
};
pub async fn connect_and_login(
- jid: &mut JID,
+ jid: &JID,
+ // jid: &mut JID,
password: impl AsRef<str>,
- server: &mut String,
-) -> Result<BoundJabberStream> {
+ // server: &mut String,
+) -> Result<(BoundJabberStream, FullJID)> {
let auth = SASLConfig::with_credentials(
None,
jid.localpart.clone().ok_or(Error::NoLocalpart)?,
password.as_ref().to_string(),
)
.map_err(|e| Error::SASL(e.into()))?;
+ let mut server = jid.domainpart.clone();
let mut conn_state = Connecting::start(&server).await?;
loop {
match conn_state {
Connecting::InsecureConnectionEstablised(tcp_stream) => {
conn_state = Connecting::InsecureStreamStarted(
- JabberStream::start_stream(Connection::Unencrypted(tcp_stream), server).await?,
+ JabberStream::start_stream(Connection::Unencrypted(tcp_stream), &mut server)
+ .await?,
)
}
Connecting::InsecureStreamStarted(jabber_stream) => {
@@ -46,8 +50,9 @@ pub async fn connect_and_login(
))
}
Connecting::ConnectionEstablished(connection) => {
- conn_state =
- Connecting::StreamStarted(JabberStream::start_stream(connection, server).await?)
+ conn_state = Connecting::StreamStarted(
+ JabberStream::start_stream(connection, &mut server).await?,
+ )
}
Connecting::StreamStarted(jabber_stream) => {
conn_state = Connecting::GotFeatures(jabber_stream.get_features().await?)
@@ -68,7 +73,7 @@ pub async fn connect_and_login(
)
}
Connecting::Bind(jabber_stream) => {
- return Ok(jabber_stream.bind(jid).await?.to_bound_jabber());
+ return Ok(jabber_stream.bind(jid).await?);
}
}
}
diff --git a/luz/src/client/ws.rs b/luz/src/client/ws.rs
index 13c3cdf..0ad8d0e 100644
--- a/luz/src/client/ws.rs
+++ b/luz/src/client/ws.rs
@@ -1,3 +1,4 @@
+use jid::FullJID;
use rsasl::config::SASLConfig;
use stanza::{
sasl::Mechanisms,
@@ -9,22 +10,22 @@ use crate::{
};
pub async fn connect_and_login(
- jid: &mut JID,
+ jid: &JID,
password: impl AsRef<str>,
- server: &mut String,
-) -> Result<BoundJabberStream> {
+) -> Result<(BoundJabberStream, FullJID)> {
let auth = SASLConfig::with_credentials(
None,
jid.localpart.clone().ok_or(Error::NoLocalpart)?,
password.as_ref().to_string(),
)
.map_err(|e| Error::SASL(e.into()))?;
+ let mut server = jid.domainpart.clone();
let mut conn_state = Connecting::start(&server).await?;
loop {
match conn_state {
Connecting::ConnectionEstablished(ws) => {
conn_state =
- Connecting::StreamStarted(JabberStream::start_stream(ws, server).await?)
+ Connecting::StreamStarted(JabberStream::start_stream(ws, &mut server).await?)
}
Connecting::StreamStarted(jabber_stream) => {
conn_state = Connecting::GotFeatures(jabber_stream.get_features().await?)
@@ -45,7 +46,7 @@ pub async fn connect_and_login(
)
}
Connecting::Bind(jabber_stream) => {
- return Ok(jabber_stream.bind(jid).await?.to_bound_jabber());
+ return Ok(jabber_stream.bind(jid).await?);
}
}
}
diff --git a/luz/src/connection/tcp.rs b/luz/src/connection/tcp.rs
index a9e81c3..7409a47 100644
--- a/luz/src/connection/tcp.rs
+++ b/luz/src/connection/tcp.rs
@@ -96,7 +96,7 @@ impl Connection {
// }
pub async fn connect_user(jid: impl AsRef<str>) -> Result<Self> {
- let jid: JID = JID::from_str(jid.as_ref())?;
+ let jid: JID = JID::from_str(jid.as_ref()).map_err(|e| Error::JID(e.into()))?;
let server = jid.domainpart.clone();
Self::connect(&server).await
}
diff --git a/luz/src/error.rs b/luz/src/error.rs
index fcb32a0..d9b2930 100644
--- a/luz/src/error.rs
+++ b/luz/src/error.rs
@@ -1,7 +1,7 @@
use std::str::Utf8Error;
use std::sync::Arc;
-use jid::ParseError;
+use jid::JIDError;
use rsasl::mechname::MechanismNameError;
use stanza::client::error::Error as ClientError;
use stanza::sasl::Failure;
@@ -36,7 +36,7 @@ pub enum Error {
#[error("sasl error: {0}")]
SASL(#[from] SASLError),
#[error("jid error: {0}")]
- JID(#[from] ParseError),
+ JID(#[from] JIDError),
#[error("client stanza error: {0}")]
ClientError(#[from] ClientError),
#[error("stream error: {0}")]
diff --git a/luz/src/jabber_stream.rs b/luz/src/jabber_stream.rs
index f77e6a9..490a0f7 100644
--- a/luz/src/jabber_stream.rs
+++ b/luz/src/jabber_stream.rs
@@ -7,7 +7,8 @@ mod ws;
use std::str::{self, FromStr};
use std::sync::Arc;
-use jid::JID;
+use bound_stream::BoundJabberStream;
+use jid::{FullJID, JID};
use peanuts::IntoElement;
#[cfg(target_arch = "wasm32")]
use peanuts::WebSocketOnMessageRead;
@@ -147,9 +148,9 @@ impl JabberStream {
}
#[instrument]
- pub async fn bind(mut self, jid: &mut JID) -> Result<Self> {
+ pub async fn bind(mut self, jid: &JID) -> Result<(BoundJabberStream, FullJID)> {
let iq_id = uuid::Uuid::new_v4().to_string();
- if let Some(resource) = &jid.resourcepart {
+ if let Some(resource) = &jid.resourcepart() {
let iq = Iq {
from: None,
id: iq_id.clone(),
@@ -176,8 +177,7 @@ impl JabberStream {
})),
errors: _,
} if id == iq_id => {
- *jid = new_jid;
- return Ok(self);
+ return Ok((self.to_bound_jabber(), new_jid));
}
Iq {
from: _,
@@ -219,8 +219,7 @@ impl JabberStream {
})),
errors: _,
} if id == iq_id => {
- *jid = new_jid;
- return Ok(self);
+ return Ok((self.to_bound_jabber(), new_jid));
}
Iq {
from: _,
@@ -257,7 +256,11 @@ impl JabberStream {
id: None,
lang: None,
version: Some("1.0".to_string()),
- to: Some(JID::from_str(server.as_ref())?),
+ to: Some(
+ JID::from_str(server.as_ref())
+ .map_err(|e| Error::JID(e.into()))?
+ .try_into()?,
+ ),
};
writer.write(&open).await?;
@@ -290,7 +293,9 @@ impl JabberStream {
// opening stream element
let stream = Stream::new_client(
None,
- JID::from_str(server.as_ref())?,
+ JID::from_str(server.as_ref())
+ .map_err(|e| Error::JID(e.into()))?
+ .try_into()?,
None,
"en".to_string(),
);
diff --git a/stanza/src/bind.rs b/stanza/src/bind.rs
index 3ce2246..0f0f681 100644
--- a/stanza/src/bind.rs
+++ b/stanza/src/bind.rs
@@ -1,4 +1,4 @@
-use jid::JID;
+use jid::FullJID;
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind";
@@ -54,7 +54,7 @@ impl IntoElement for BindType {
// minLength 8 maxLength 3071
#[derive(Clone, Debug)]
-pub struct FullJidType(pub JID);
+pub struct FullJidType(pub FullJID);
impl FromElement for FullJidType {
fn from_element(mut element: peanuts::Element) -> peanuts::DeserializeResult<Self> {
diff --git a/stanza/src/rfc_7395.rs b/stanza/src/rfc_7395.rs
index 64d9f70..73e947d 100644
--- a/stanza/src/rfc_7395.rs
+++ b/stanza/src/rfc_7395.rs
@@ -1,12 +1,12 @@
-use jid::JID;
+use jid::BareJID;
use peanuts::{Element, ElementBuilder, FromElement, IntoElement};
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-framing";
#[derive(Debug)]
pub struct Open {
- pub from: Option<JID>,
- pub to: Option<JID>,
+ pub from: Option<BareJID>,
+ pub to: Option<BareJID>,
pub id: Option<String>,
pub version: Option<String>,
pub lang: Option<String>,
@@ -46,8 +46,8 @@ impl IntoElement for Open {
#[derive(Debug, Default)]
pub struct Close {
- pub from: Option<JID>,
- pub to: Option<JID>,
+ pub from: Option<BareJID>,
+ pub to: Option<BareJID>,
pub id: Option<String>,
pub version: Option<String>,
pub lang: Option<String>,
diff --git a/stanza/src/roster.rs b/stanza/src/roster.rs
index 14f65ef..dcbf017 100644
--- a/stanza/src/roster.rs
+++ b/stanza/src/roster.rs
@@ -1,6 +1,6 @@
use std::str::FromStr;
-use jid::JID;
+use jid::BareJID;
use peanuts::{DeserializeError, Element, FromElement, IntoElement};
pub const XMLNS: &str = "jabber:iq:roster";
@@ -38,7 +38,7 @@ pub struct Item {
/// signals subscription sub-states (server only)
pub ask: bool,
/// uniquely identifies item
- pub jid: JID,
+ pub jid: BareJID,
/// handle that is determined by user, not contact
pub name: Option<String>,
/// state of the presence subscription
diff --git a/stanza/src/stream.rs b/stanza/src/stream.rs
index 5be235a..e2f4f9b 100644
--- a/stanza/src/stream.rs
+++ b/stanza/src/stream.rs
@@ -1,6 +1,6 @@
use std::fmt::Display;
-use jid::JID;
+use jid::BareJID;
use peanuts::{Element, ElementBuilder, FromElement, IntoElement};
use thiserror::Error;
@@ -18,8 +18,8 @@ pub const XMLNS: &str = "http://etherx.jabber.org/streams";
// #[peanuts(xmlns = XMLNS)]
#[derive(Debug)]
pub struct Stream {
- pub from: Option<JID>,
- to: Option<JID>,
+ pub from: Option<BareJID>,
+ to: Option<BareJID>,
id: Option<String>,
version: Option<String>,
// TODO: lang enum
@@ -64,8 +64,8 @@ impl IntoElement for Stream {
impl<'s> Stream {
pub fn new(
- from: Option<JID>,
- to: Option<JID>,
+ from: Option<BareJID>,
+ to: Option<BareJID>,
id: Option<String>,
version: Option<String>,
lang: Option<String>,
@@ -81,7 +81,12 @@ impl<'s> Stream {
/// For initial stream headers, the initiating entity SHOULD include the 'xml:lang' attribute.
/// For privacy, it is better to not set `from` when sending a client stanza over an unencrypted connection.
- pub fn new_client(from: Option<JID>, to: JID, id: Option<String>, lang: String) -> Self {
+ pub fn new_client(
+ from: Option<BareJID>,
+ to: BareJID,
+ id: Option<String>,
+ lang: String,
+ ) -> Self {
Self {
from,
to: Some(to),
diff --git a/stanza/src/xep_0060/owner.rs b/stanza/src/xep_0060/owner.rs
index 0617712..4876bf5 100644
--- a/stanza/src/xep_0060/owner.rs
+++ b/stanza/src/xep_0060/owner.rs
@@ -1,6 +1,6 @@
use std::str::FromStr;
-use jid::JID;
+use jid::{BareJID, JID};
use peanuts::{DeserializeError, Element, FromElement, IntoElement};
use crate::xep_0004::X;
@@ -85,7 +85,7 @@ impl IntoElement for Affiliations {
#[derive(Clone, Debug)]
pub struct Affiliation {
affiliation: AffiliationType,
- jid: JID,
+ jid: BareJID,
}
impl FromElement for Affiliation {