[FIX] Migrated to Tensamin Transport Protocol

This commit is contained in:
Alex Emmet 2026-02-27 23:44:47 +01:00
commit c9d21fd3c6
24 changed files with 1451 additions and 2252 deletions

618
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,9 @@ version = "0.1.0"
edition = "2024"
[dependencies]
epsilon-core = { git = "https://github.com/Tensamin/Epsilon.git", package = "epsilon-core" }
epsilon-native = { git = "https://github.com/Tensamin/Epsilon.git", package = "epsilon-native" }
ansi_term = "*"
async-tungstenite = { version = "0.32.1", features = [
"futures-03-sink",
@ -56,7 +59,7 @@ pkcs8 = { version = "*", features = ["alloc"] }
rand = "0.8"
rand_core = { version = "0.6", features = ["getrandom", "std"] }
reqwest = "*"
rustls = { version = "*", default-features = false, features = ["ring"] }
rustls = { version = "0.23.37", default-features = false, features = ["ring"] }
serde = { version = "1.0.219", features = ["derive"] }
sha2 = "*"
sys-info = "*"
@ -85,7 +88,9 @@ cbc = "0.1.2"
aes = "0.8.4"
strum = "0.27.2"
strum_macros = "0.27.2"
livekit = "0.7.25"
livekit-api = { version = "0.4.10", features = ["native-tls"] }
livekit = "0.7.32"
livekit-api = { version = "0.4.14", features = ["native-tls"] }
hyper = { version = "1.8.1", features = ["http1", "http2"] }
livekit-protocol = "0.6.0"
livekit-protocol = "0.7.1"
rustls-pemfile = "2.2.0"
quinn = "0.11.9"

View file

@ -1,27 +1,24 @@
use async_tungstenite::tungstenite::Message;
use async_tungstenite::{WebSocketReceiver, WebSocketSender};
use json::JsonValue;
use json::number::Number;
use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue};
use epsilon_native::{Receiver, Sender};
use std::str::FromStr;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::time::Duration;
use tokio::sync::RwLock;
use tokio_util::compat::Compat;
use tungstenite::Utf8Bytes;
use uuid::Uuid;
use crate::anonymous_clients::anonymous_manager::{self, generate_username};
use crate::calls::call_manager;
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
use crate::omega::omega_connection::get_omega_connection;
use crate::rho::connection::GeneralConnection;
use crate::rho::rho_manager;
use crate::util::logger::PrintType;
use crate::{log_in, log_out};
use crate::{log_cv_in, log_cv_out, log_out};
pub struct AnonymousClientConnection {
pub sender: Arc<RwLock<WebSocketSender<Compat<tokio::net::TcpStream>>>>,
pub receiver: Arc<RwLock<WebSocketReceiver<Compat<tokio::net::TcpStream>>>>,
pub user_id: Arc<RwLock<i64>>,
user_id: u64,
pub sender: Arc<Sender>,
pub receiver: Arc<Receiver>,
pub ping: Arc<RwLock<i64>>,
pub interested_users: Arc<RwLock<Vec<i64>>>,
is_open: Arc<RwLock<bool>>,
@ -31,33 +28,33 @@ pub struct AnonymousClientConnection {
}
impl AnonymousClientConnection {
/// Create a new AnonymousClientConnection
pub fn new(
sender: WebSocketSender<Compat<tokio::net::TcpStream>>,
receiver: WebSocketReceiver<Compat<tokio::net::TcpStream>>,
) -> Arc<Self> {
pub async fn from_general(general: Arc<GeneralConnection>, user_id: u64) -> Arc<Self> {
let username: String = generate_username();
Arc::new(Self {
sender: Arc::new(RwLock::new(sender)),
receiver: Arc::new(RwLock::new(receiver)),
user_id: Arc::new(RwLock::new(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as i64,
)),
ping: Arc::new(RwLock::new(-1)),
user_id: user_id,
ping: Arc::new(RwLock::new(0)),
interested_users: Arc::new(RwLock::new(Vec::new())),
is_open: Arc::new(RwLock::new(true)),
sender: general.sender.clone(),
receiver: general.receiver.clone(),
user_name: Arc::new(RwLock::new(username.to_lowercase())),
display_name: Arc::new(RwLock::new(username)),
avatar: Arc::new(RwLock::new(String::new())),
})
}
pub fn start(self: Arc<Self>) {
let self_clone = self.clone();
tokio::spawn(async move {
while let Ok(cv) = self_clone.receiver.receive().await {
self_clone.clone().handle_message(cv).await;
}
});
}
/// Get the user ID
pub async fn get_user_id(&self) -> i64 {
*self.user_id.read().await
pub fn get_user_id(&self) -> u64 {
self.user_id
}
/// Get the user name
@ -79,66 +76,35 @@ impl AnonymousClientConnection {
self.avatar.read().await.clone()
}
/// Send a string message to the client
pub async fn send_message_str(self: Arc<Self>, message: &str) {
let mut session = self.sender.write().await;
if let Err(e) = session
.send(Message::Text(Utf8Bytes::from(message.to_string())))
.await
{
log_out!(
self.get_user_id().await,
PrintType::Client,
"Failed to send message to anonymous client: {}",
e,
);
}
}
/// Send a CommunicationValue to the client
pub async fn send_message(self: Arc<Self>, cv: &CommunicationValue) {
if !*self.is_open.read().await {
log_out!(
self.get_user_id().await,
self.user_id as i64,
PrintType::Client,
"Attempted to send message to a closed connection."
);
return;
}
if !cv.is_type(CommunicationType::pong) {
log_out!(
self.get_user_id().await,
PrintType::Client,
"{}",
&cv.to_json().to_string()
);
log_cv_out!(PrintType::Client, &cv);
}
self.send_message_str(&cv.to_json().to_string()).await;
self.sender.send(&cv).await;
}
/// Handle incoming message from client
pub async fn handle_message(self: Arc<Self>, message: Utf8Bytes) {
pub async fn handle_message(self: Arc<Self>, cv: CommunicationValue) {
tokio::spawn(async move {
let cv = CommunicationValue::from_json(&message);
if cv.is_type(CommunicationType::ping) {
self.handle_ping(cv).await;
return;
}
log_in!(
self.get_user_id().await,
PrintType::Client,
"Anonymous: {}",
&cv.to_json().to_string()
);
log_cv_in!(PrintType::Client, &cv);
if cv.is_type(CommunicationType::identification) {
let call_id = Uuid::parse_str(
cv.get_data(DataTypes::call_id)
.unwrap_or(&JsonValue::Null)
.as_str()
.unwrap_or(""),
)
.unwrap_or(Uuid::new_v4());
let call_id =
Uuid::parse_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or(""))
.unwrap_or(Uuid::new_v4());
let call = if let Some(call) = call_manager::get_call(call_id).await {
if call.is_anonymous().await {
@ -160,79 +126,62 @@ impl AnonymousClientConnection {
return;
};
let mut invited = JsonValue::new_array();
let mut invited = Vec::new();
for call_invitee in call.members.read().await.clone() {
let call_invitee_cv = get_omega_connection()
.await_response(
&CommunicationValue::new(CommunicationType::get_user_data).add_data(
DataTypes::user_id,
JsonValue::from(call_invitee.user_id),
DataValue::Number(call_invitee.user_id as i64),
),
Some(Duration::from_secs(2)),
)
.await
.unwrap();
let mut json_invitee = JsonValue::new_object();
let _ = json_invitee.insert(
"user_id",
call_invitee_cv
.get_data(DataTypes::user_id)
.unwrap_or(&JsonValue::Null)
.clone(),
);
let _ = json_invitee.insert(
"username",
call_invitee_cv
.get_data(DataTypes::username)
.unwrap_or(&JsonValue::Null)
.clone(),
);
let _ = json_invitee.insert(
"display",
call_invitee_cv
.get_data(DataTypes::display)
.unwrap_or(&JsonValue::Null)
.clone(),
);
let _ = json_invitee.insert(
"avatar",
call_invitee_cv
.get_data(DataTypes::avatar)
.unwrap_or(&JsonValue::Null)
.clone(),
);
let mut json_invitee = Vec::new();
let _ = json_invitee.push((
DataTypes::user_id,
call_invitee_cv.get_data(DataTypes::user_id).clone(),
));
let _ = json_invitee.push((
DataTypes::username,
call_invitee_cv.get_data(DataTypes::username).clone(),
));
let _ = json_invitee.push((
DataTypes::display,
call_invitee_cv.get_data(DataTypes::display).clone(),
));
let _ = json_invitee.push((
DataTypes::avatar,
call_invitee_cv.get_data(DataTypes::avatar).clone(),
));
let _ = invited.push(json_invitee);
let _ = invited.push(DataValue::Container(json_invitee));
}
let token = call.create_anonymous_token(self.get_user_id().await).await;
let token = call.create_anonymous_token(self.get_user_id()).await;
let mut serialized = JsonValue::new_object();
let _ = serialized.insert("call_id", JsonValue::String(call_id.to_string()));
let _ = serialized.insert("call_invited", invited.clone());
let _ = serialized.insert("call_members", invited);
let _ = serialized.insert("call_token", JsonValue::String(token.unwrap()));
let mut serialized = Vec::new();
let _ = serialized.push((DataTypes::call_id, DataValue::Str(call_id.to_string())));
let _ =
serialized.push((DataTypes::call_invited, DataValue::Array(invited.clone())));
let _ = serialized.push((DataTypes::call_members, DataValue::Array(invited)));
let _ = serialized.push((DataTypes::call_token, DataValue::Str(token.unwrap())));
self.clone()
.send_message(
&&CommunicationValue::new(CommunicationType::identification_response)
.with_id(cv.get_id())
.add_data(
DataTypes::user_id,
JsonValue::from(self.get_user_id().await),
)
.add_data(DataTypes::user_id, DataValue::Number(self.user_id as i64))
.add_data(
DataTypes::username,
JsonValue::String(self.clone().get_user_name().await),
DataValue::Str(self.clone().get_user_name().await),
)
.add_data(
DataTypes::display,
JsonValue::String(self.get_display_name().await),
DataValue::Str(self.get_display_name().await),
)
.add_data(
DataTypes::avatar,
JsonValue::String(self.get_avatar().await),
)
.add_data(DataTypes::call_state, serialized),
.add_data(DataTypes::avatar, DataValue::Str(self.get_avatar().await))
.add_data(DataTypes::call_state, DataValue::Container(serialized)),
)
.await;
}
@ -271,11 +220,7 @@ impl AnonymousClientConnection {
}
if cv.is_type(CommunicationType::change_user_data) {
if let Some(display_name) = cv
.get_data(DataTypes::display)
.unwrap_or(&JsonValue::Null)
.as_str()
{
if let Some(display_name) = cv.get_data(DataTypes::display).as_str() {
let _ = self.set_display_name(display_name.to_string()).await;
}
@ -284,17 +229,9 @@ impl AnonymousClientConnection {
if cv.is_type(CommunicationType::get_user_data) {
if let Some(anonymous) = {
if let Some(user_id) = cv
.get_data(DataTypes::user_id)
.unwrap_or(&JsonValue::Null)
.as_i64()
{
anonymous_manager::get_anonymous_user(user_id).await
} else if let Some(username) = cv
.get_data(DataTypes::username)
.unwrap_or(&JsonValue::Null)
.as_str()
{
if let Some(user_id) = cv.get_data(DataTypes::user_id).as_number() {
anonymous_manager::get_anonymous_user(user_id as u64).await
} else if let Some(username) = cv.get_data(DataTypes::username).as_str() {
anonymous_manager::get_anonymous_user_by_name(username.to_string()).await
} else {
None
@ -302,14 +239,23 @@ impl AnonymousClientConnection {
} {
let response = CommunicationValue::new(CommunicationType::get_user_data)
.with_id(cv.get_id())
.add_data_str(DataTypes::username, anonymous.get_user_name().await)
.add_data(
DataTypes::username,
DataValue::Str(anonymous.get_user_name().await),
)
.add_data(
DataTypes::user_id,
JsonValue::Number(Number::from(anonymous.get_user_id().await)),
DataValue::Number(anonymous.user_id as i64),
)
.add_data_str(DataTypes::display, anonymous.get_display_name().await)
.add_data_str(DataTypes::user_state, "online".to_string())
.add_data_str(DataTypes::avatar, anonymous.get_avatar().await);
.add_data(
DataTypes::display,
DataValue::Str(anonymous.get_display_name().await),
)
.add_data(DataTypes::user_state, DataValue::Str("online".to_string()))
.add_data(
DataTypes::avatar,
DataValue::Str(anonymous.get_avatar().await),
);
self.send_message(&response).await;
@ -330,10 +276,7 @@ impl AnonymousClientConnection {
let client_for_closure = self.clone();
tokio::spawn(async move {
let response_cv = get_omega_connection()
.await_response(
&cv.with_sender(*self.user_id.read().await),
Some(Duration::from_secs(20)),
)
.await_response(&cv.with_sender(self.user_id), Some(Duration::from_secs(20)))
.await;
if let Ok(response_cv) = response_cv {
client_for_closure.send_message(&response_cv).await;
@ -344,7 +287,7 @@ impl AnonymousClientConnection {
/// Handle ping message
async fn handle_ping(self: Arc<Self>, cv: CommunicationValue) {
// Update our ping if provided
if let Some(last_ping) = cv.get_data(DataTypes::last_ping) {
if let DataValue::Number(last_ping) = cv.get_data(DataTypes::last_ping) {
if let Ok(ping_val) = last_ping.to_string().parse::<i64>() {
let mut ping_guard = self.ping.write().await;
*ping_guard = ping_val;
@ -367,11 +310,7 @@ impl AnonymousClientConnection {
/// Handle call invite
async fn handle_call_invite(self: Arc<Self>, cv: CommunicationValue) {
let receiver_id: i64 = cv
.get_data(DataTypes::receiver_id)
.unwrap_or(&json::JsonValue::Number(Number::from(0)))
.as_i64()
.unwrap_or(0);
let receiver_id: i64 = cv.get_data(DataTypes::receiver_id).as_number().unwrap_or(0);
if receiver_id == 0 {
self.send_error_response(&cv.get_id(), CommunicationType::error_no_user_id)
.await;
@ -379,7 +318,7 @@ impl AnonymousClientConnection {
}
let call_id = match cv.get_data(DataTypes::call_id) {
Some(id_str) => match Uuid::parse_str(&id_str.to_string()) {
DataValue::Str(id_str) => match Uuid::parse_str(&id_str.to_string()) {
Ok(id) => id,
Err(_) => {
self.send_error_response(
@ -397,8 +336,7 @@ impl AnonymousClientConnection {
}
};
let invited =
call_manager::add_invite(call_id, *self.user_id.read().await, receiver_id).await;
let invited = call_manager::add_invite(call_id, self.user_id, receiver_id as u64).await;
if !invited {
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_call_id)
.await;
@ -416,15 +354,18 @@ impl AnonymousClientConnection {
};
// Get sender user ID
let sender_id = self.get_user_id().await;
let sender_id = self.get_user_id();
// Create and send call distribution message
let forward = CommunicationValue::new(CommunicationType::call_invite)
.with_receiver(receiver_id)
.with_receiver(receiver_id as u64)
.with_sender(sender_id)
.add_data_str(DataTypes::call_id, call_id.to_string())
.add_data_str(DataTypes::receiver_id, receiver_id.to_string())
.add_data_str(DataTypes::sender_id, sender_id.to_string());
.add_data(DataTypes::call_id, DataValue::Str(call_id.to_string()))
.add_data(
DataTypes::receiver_id,
DataValue::Str(receiver_id.to_string()),
)
.add_data(DataTypes::sender_id, DataValue::Str(sender_id.to_string()));
target_rho.message_to_client(forward).await;
@ -434,10 +375,10 @@ impl AnonymousClientConnection {
/// Handle get call request
async fn handle_get_call(self: Arc<Self>, cv: CommunicationValue) {
let user_id = self.get_user_id().await;
let user_id = self.get_user_id();
let call_id = match cv.get_data(DataTypes::call_id) {
Some(id_str) => match Uuid::parse_str(&id_str.to_string()) {
DataValue::Str(id_str) => match Uuid::parse_str(&id_str.to_string()) {
Ok(id) => id,
Err(_) => {
self.send_error_response(&cv.get_id(), CommunicationType::error)
@ -456,7 +397,7 @@ impl AnonymousClientConnection {
let response = CommunicationValue::new(CommunicationType::call_token)
.with_id(cv.get_id())
.with_receiver(user_id)
.add_data_str(DataTypes::call_token, token);
.add_data(DataTypes::call_token, DataValue::Str(token.to_string()));
self.send_message(&response).await;
} else {
self.send_error_response(&cv.get_id(), CommunicationType::error)
@ -465,33 +406,20 @@ impl AnonymousClientConnection {
}
}
async fn handle_call_timeout_user(self: Arc<Self>, cv: CommunicationValue) {
let call_id = Uuid::from_str(
cv.get_data(DataTypes::call_id)
.unwrap_or(&JsonValue::Null)
.as_str()
.unwrap_or(""),
)
.unwrap();
let user_id = cv
.get_data(DataTypes::user_id)
.unwrap_or(&JsonValue::Null)
.as_i64()
.unwrap_or(0);
let untill = cv
.get_data(DataTypes::untill)
.unwrap_or(&JsonValue::Null)
.as_i64()
.unwrap_or(0);
let call_id =
Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap();
let user_id = cv.get_data(DataTypes::user_id).as_number().unwrap_or(0);
let untill = cv.get_data(DataTypes::untill).as_number().unwrap_or(0);
let call = call_manager::get_call(call_id).await;
if let Some(call) = call {
if call
.get_caller(self.get_user_id().await)
.get_caller(self.get_user_id())
.await
.unwrap()
.has_admin()
{
call.get_caller(user_id)
call.get_caller(user_id as u64)
.await
.unwrap()
.set_timeout(untill)
@ -500,38 +428,25 @@ impl AnonymousClientConnection {
}
}
async fn handle_call_disconnect_user(self: Arc<Self>, cv: CommunicationValue) {
let call_id = Uuid::from_str(
cv.get_data(DataTypes::call_id)
.unwrap_or(&JsonValue::Null)
.as_str()
.unwrap_or(""),
)
.unwrap();
let user_id = cv
.get_data(DataTypes::user_id)
.unwrap_or(&JsonValue::Null)
.as_i64()
.unwrap_or(0);
let call_id =
Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap();
let user_id = cv.get_data(DataTypes::user_id).as_number().unwrap_or(0);
let call = call_manager::get_call(call_id).await;
if let Some(call) = call {
if call
.get_caller(self.get_user_id().await)
.get_caller(self.get_user_id())
.await
.unwrap()
.has_admin()
{
call.remove_caller(user_id).await;
call.remove_caller(user_id as u64).await;
}
}
}
/// Send error response
async fn send_error_response(
self: Arc<Self>,
message_id: &Uuid,
error_type: CommunicationType,
) {
async fn send_error_response(self: Arc<Self>, message_id: &u32, error_type: CommunicationType) {
let error = CommunicationValue::new(error_type).with_id(*message_id);
self.send_message(&error).await;
}
@ -544,8 +459,7 @@ impl AnonymousClientConnection {
}
*is_open_guard = false;
let mut session = self.sender.write().await;
let _ = session.close(None).await;
let _ = self.sender.close();
}
/// Set interested users list
@ -563,8 +477,8 @@ impl AnonymousClientConnection {
let interested_guard = self.clone().get_interested_users().await;
if interested_guard.contains(&user_id) {
let notification = CommunicationValue::new(CommunicationType::client_changed)
.add_data_str(DataTypes::user_id, user_id.to_string())
.add_data_str(DataTypes::user_state, format!("online"));
.add_data(DataTypes::user_id, DataValue::Str(user_id.to_string()))
.add_data(DataTypes::user_state, DataValue::Str("online".to_string()));
self.send_message(&notification).await;
}
@ -583,7 +497,7 @@ impl Clone for AnonymousClientConnection {
Self {
sender: Arc::clone(&self.sender),
receiver: Arc::clone(&self.receiver),
user_id: Arc::clone(&self.user_id),
user_id: self.user_id,
ping: Arc::clone(&self.ping),
interested_users: Arc::clone(&self.interested_users),
is_open: Arc::clone(&self.is_open),

View file

@ -6,18 +6,18 @@ use std::sync::Arc;
use crate::anonymous_clients::anonymous_client_connection::AnonymousClientConnection;
static ANONYMOUS_USERS: Lazy<DashMap<i64, Arc<AnonymousClientConnection>>> =
static ANONYMOUS_USERS: Lazy<DashMap<u64, Arc<AnonymousClientConnection>>> =
Lazy::new(|| DashMap::new());
pub async fn add_anonymous_user(connection: Arc<AnonymousClientConnection>) {
ANONYMOUS_USERS.insert(connection.get_user_id().await, connection);
ANONYMOUS_USERS.insert(connection.get_user_id(), connection);
}
pub async fn remove_anonymous_user(user_id: i64) {
pub async fn remove_anonymous_user(user_id: u64) {
ANONYMOUS_USERS.remove(&user_id);
}
pub async fn get_anonymous_user(user_id: i64) -> Option<Arc<AnonymousClientConnection>> {
pub async fn get_anonymous_user(user_id: u64) -> Option<Arc<AnonymousClientConnection>> {
ANONYMOUS_USERS.get(&user_id).map(|c| c.clone())
}

View file

@ -1,11 +1,11 @@
use json::JsonValue;
use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue};
use std::{env, sync::Arc, time::Duration};
use tokio::sync::RwLock;
use uuid::Uuid;
use crate::{
calls::{call_util, caller::Caller},
data::communication::{CommunicationType, CommunicationValue, DataTypes},
omega::omega_connection::get_omega_connection,
};
@ -28,7 +28,7 @@ impl CallGroup {
}
}
pub async fn get_caller(&self, user_id: i64) -> Option<Arc<Caller>> {
pub async fn get_caller(&self, user_id: u64) -> Option<Arc<Caller>> {
self.members
.read()
.await
@ -62,7 +62,7 @@ impl CallGroup {
let response_cv = get_omega_connection()
.await_response(
&CommunicationValue::new(CommunicationType::shorten_link)
.add_data(DataTypes::link, JsonValue::from(long_link)),
.add_data(DataTypes::link, DataValue::Str(long_link)),
Some(Duration::from_secs(20)),
)
.await;
@ -70,7 +70,6 @@ impl CallGroup {
*self.short_link.write().await = Some(
response
.get_data(DataTypes::link)
.unwrap()
.as_str()
.unwrap()
.to_string(),
@ -84,7 +83,7 @@ impl CallGroup {
}
}
pub async fn create_anonymous_token(&self, user_id: i64) -> Option<String> {
pub async fn create_anonymous_token(&self, user_id: u64) -> Option<String> {
if self.is_anonymous().await {
if let Ok(token) = call_util::create_token(user_id, self.call_id, false) {
return Some(token);
@ -93,7 +92,7 @@ impl CallGroup {
None
}
pub async fn remove_caller(&self, user_id: i64) {
pub async fn remove_caller(&self, user_id: u64) {
let _ = call_util::remove_participant(self.call_id, user_id).await;
self.members
.write()

View file

@ -7,7 +7,7 @@ use crate::calls::{call_group::CallGroup, caller::Caller};
pub static CALL_GROUPS: Lazy<DashMap<Uuid, Arc<CallGroup>>> = Lazy::new(|| DashMap::new());
pub async fn get_call_invites(user_id: i64) -> Vec<Arc<Caller>> {
pub async fn get_call_invites(user_id: u64) -> Vec<Arc<Caller>> {
let mut callers = Vec::new();
for (_, cg) in CALL_GROUPS.clone().into_iter() {
let members = cg.members.read().await;
@ -28,7 +28,7 @@ pub async fn get_call(call_id: Uuid) -> Option<Arc<CallGroup>> {
}
}
pub async fn get_call_groups(user_id: i64) -> Vec<Arc<CallGroup>> {
pub async fn get_call_groups(user_id: u64) -> Vec<Arc<CallGroup>> {
let mut call_groups = Vec::new();
for (_, cg) in CALL_GROUPS.clone().into_iter() {
let is_member = {
@ -43,7 +43,7 @@ pub async fn get_call_groups(user_id: i64) -> Vec<Arc<CallGroup>> {
call_groups
}
pub async fn get_call_token(user_id: i64, call_id: Uuid) -> Option<String> {
pub async fn get_call_token(user_id: u64, call_id: Uuid) -> Option<String> {
if let Some(cg) = CALL_GROUPS.get(&call_id) {
let mut members = cg.members.write().await;
@ -80,7 +80,7 @@ pub async fn get_call_token(user_id: i64, call_id: Uuid) -> Option<String> {
Some(caller.create_token())
}
pub async fn add_invite(call_id: Uuid, inviter_id: i64, invitee_id: i64) -> bool {
pub async fn add_invite(call_id: Uuid, inviter_id: u64, invitee_id: u64) -> bool {
if let Some(cg) = CALL_GROUPS.get(&call_id) {
let mut members = cg.members.write().await;

View file

@ -35,7 +35,7 @@ pub fn get_livekit() -> Result<(String, String, String), ()> {
Ok((hostname, api_key, api_secret))
}
pub fn create_token(user_id: i64, call_id: Uuid, has_admin: bool) -> Result<String, ()> {
pub fn create_token(user_id: u64, call_id: Uuid, has_admin: bool) -> Result<String, ()> {
let (_, api_key, api_secret) = get_livekit()?;
let token = access_token::AccessToken::with_api_key(&api_key, &api_secret)
@ -69,7 +69,7 @@ pub async fn get_room(call_id: Uuid) -> Result<(RoomClient, Room), ()> {
return Err(());
}
pub async fn remove_participant(call_id: Uuid, user_id: i64) -> Result<(), ()> {
pub async fn remove_participant(call_id: Uuid, user_id: u64) -> Result<(), ()> {
if let Ok((hostname, api_key, api_secret)) = get_livekit() {
let room_service = RoomClient::with_api_key(&hostname, &api_key, &api_secret);
if let Ok(_) = room_service

View file

@ -6,14 +6,14 @@ use uuid::Uuid;
use crate::calls::call_util;
pub struct Caller {
pub user_id: i64,
pub user_id: u64,
pub call_id: Uuid,
pub has_admin: bool,
pub timeout: RwLock<i64>,
}
impl Caller {
pub fn new(user_id: i64, call_id: Uuid, has_admin: bool) -> Self {
pub fn new(user_id: u64, call_id: Uuid, has_admin: bool) -> Self {
Caller {
user_id,
call_id,

View file

@ -1,403 +0,0 @@
use json::number::Number;
use json::{Array, JsonValue, object, parse};
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
use uuid::Uuid;
#[derive(Eq, Hash, PartialEq, EnumIter, Clone, Debug)]
#[allow(non_camel_case_types, dead_code)]
pub enum DataTypes {
error_type,
accepted_ids,
uuid,
register_id,
link,
settings,
settings_name,
chat_partner_id,
chat_partner_name,
iota_id,
user_id,
user_ids,
iota_ids,
user_state,
user_states,
user_pings,
call_state,
screen_share,
private_key_hash,
accepted,
accepted_profiles,
denied_profiles,
content,
messages,
notifications,
send_time,
get_time,
get_variant,
shared_secret_own,
shared_secret_other,
shared_secret_sign,
shared_secret,
call_id,
call_token,
untill,
enabled,
start_date,
end_date,
receiver_id,
sender_id,
signature,
signed,
message,
message_state,
last_ping,
ping_iota,
ping_clients,
matches,
omikron,
offset,
amount,
position,
name,
path,
codec,
function,
payload,
result,
interactables,
want_to_watch,
watcher,
created_at,
username,
display,
avatar,
about,
status,
public_key,
sub_level,
sub_end,
community_address,
challenge,
community_title,
communities,
rho_connections,
user,
online_status,
omikron_id,
omikron_connections,
reset_token,
new_token,
}
impl DataTypes {
pub fn parse(p0: String) -> DataTypes {
for datatype in DataTypes::iter() {
if datatype.to_string().to_lowercase().replace('_', "")
== p0.to_lowercase().replace('_', "")
{
return datatype;
}
}
DataTypes::error_type
}
pub fn to_string(&self) -> String {
return format!("{:?}", self);
}
}
#[derive(PartialEq, Clone, EnumIter, Debug)]
#[allow(non_camel_case_types, dead_code)]
pub enum CommunicationType {
error,
error_anonymous,
error_internal,
error_invalid_data,
error_invalid_user_id,
error_invalid_omikron_id,
error_not_found,
error_not_authenticated,
error_no_iota,
error_invalid_challenge,
error_invalid_secret,
error_invalid_private_key,
error_invalid_public_key,
error_no_user_id,
error_no_call_id,
error_invalid_call_id,
success,
shorten_link,
settings_save,
settings_load,
settings_list,
message,
message_state,
message_send,
message_live,
message_other_iota,
message_chunk,
messages_get,
push_notification,
read_notification,
get_notifications,
change_confirm,
confirm_receive,
confirm_read,
get_chats,
get_states,
add_community,
remove_community,
get_communities,
challenge,
challenge_response,
register,
register_response,
identification,
identification_response,
register_iota,
register_iota_success,
ping,
pong,
add_conversation,
send_chat,
client_changed,
client_connected,
client_disconnected,
client_closed,
public_key,
private_key,
webrtc_sdp,
webrtc_ice,
start_stream,
end_stream,
watch_stream,
call_token,
call_invite,
call_disconnect_user,
call_timeout_user,
call_set_anonymous_joining,
end_call,
function,
update,
create_user,
rho_update,
user_connected,
user_disconnected,
iota_connected,
iota_disconnected,
sync_client_iota_status,
get_user_data,
get_iota_data,
iota_user_data,
change_user_data,
change_iota_data,
get_register,
complete_register_user,
complete_register_iota,
delete_user,
delete_iota,
start_register,
complete_register,
}
impl CommunicationType {
pub fn parse(p0: String) -> CommunicationType {
for datatype in CommunicationType::iter() {
if datatype.to_string().to_lowercase().replace('_', "")
== p0.to_lowercase().replace('_', "")
{
return datatype;
}
}
CommunicationType::error
}
pub fn to_string(&self) -> String {
return format!("{:?}", self);
}
}
#[derive(Debug, Clone)]
pub struct CommunicationValue {
id: Uuid,
comm_type: CommunicationType,
sender: i64,
receiver: i64,
data: HashMap<DataTypes, JsonValue>,
}
#[allow(dead_code)]
impl CommunicationValue {
pub fn new(comm_type: CommunicationType) -> Self {
Self {
id: Uuid::new_v4(),
comm_type,
sender: 0,
receiver: 0,
data: HashMap::new(),
}
}
pub fn with_id(mut self, p0: Uuid) -> Self {
self.id = p0;
self
}
pub fn get_id(&self) -> Uuid {
self.id.clone()
}
pub fn with_sender(mut self, sender: i64) -> Self {
self.sender = sender;
self
}
pub fn get_sender(&self) -> i64 {
self.sender.clone()
}
pub fn with_receiver(mut self, receiver: i64) -> Self {
self.receiver = receiver;
self
}
pub fn get_receiver(&self) -> i64 {
self.receiver.clone()
}
pub fn add_data_num(mut self, key: DataTypes, value: Number) -> Self {
self.data.insert(key, JsonValue::Number(value));
self
}
pub fn add_data_str(mut self, key: DataTypes, value: String) -> Self {
self.data.insert(key, JsonValue::String(value));
self
}
pub fn add_data(mut self, key: DataTypes, value: JsonValue) -> Self {
self.data.insert(key, value);
self
}
pub fn add_array(mut self, key: DataTypes, value: Array) -> Self {
self.data.insert(key, JsonValue::Array(value));
self
}
pub fn get_data(&self, key: DataTypes) -> Option<&JsonValue> {
self.data.get(&key)
}
pub fn get_type(&self) -> CommunicationType {
self.comm_type.clone()
}
pub fn is_type(&self, p0: CommunicationType) -> bool {
self.comm_type == p0
}
pub fn to_json(&self) -> JsonValue {
let mut jdata = object! {};
for (k, v) in &self.data {
jdata[&format!("{:?}", k)] = JsonValue::from(v.clone());
}
if self.sender > 0 && self.receiver > 0 {
object! {
id: self.id.to_string(),
type: format!("{:?}", self.comm_type),
sender: self.sender,
receiver: self.receiver,
data: jdata
}
} else if self.sender > 0 {
object! {
id: self.id.to_string(),
type: format!("{:?}", self.comm_type),
sender: self.sender,
data: jdata
}
} else if self.receiver > 0 {
object! {
id: self.id.to_string(),
type: format!("{:?}", self.comm_type),
receiver: self.receiver,
data: jdata
}
} else {
object! {
id: self.id.to_string(),
type: format!("{:?}", self.comm_type),
data: jdata
}
}
}
pub fn from_json(json_str: &str) -> Self {
if let Ok(parsed) = parse(json_str) {
let comm_type = CommunicationType::parse(parsed["type"].to_string());
let mut sender: i64 = 0;
if parsed.has_key("sender") {
sender = parsed["sender"].as_i64().unwrap_or(0);
}
let mut receiver: i64 = 0;
if parsed.has_key("receiver") {
receiver = parsed["receiver"].as_i64().unwrap_or(0);
}
let uuid =
Uuid::parse_str(parsed["id"].as_str().unwrap_or("")).unwrap_or(Uuid::new_v4());
let mut data = HashMap::new();
if parsed["data"].is_object() {
for (k, v) in parsed["data"].entries() {
data.insert(DataTypes::parse(k.to_string()), v.clone());
}
}
Self {
id: uuid,
comm_type,
sender,
receiver,
data,
}
} else {
Self {
id: Uuid::new_v4(),
comm_type: CommunicationType::error,
sender: 0,
receiver: 0,
data: HashMap::new(),
}
}
}
pub fn forward_to_other_iota(original: &mut CommunicationValue) -> CommunicationValue {
let receiver = original
.get_data(DataTypes::receiver_id)
.unwrap_or(&JsonValue::Number(Number::from(0)))
.as_i64()
.unwrap_or(0);
let now_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as i64;
let sender = original.get_sender();
CommunicationValue::new(CommunicationType::message_other_iota)
.with_id(original.get_id())
.with_receiver(receiver)
.add_data(
DataTypes::receiver_id,
JsonValue::Number(Number::from(receiver)),
)
.with_sender(sender)
.add_data(DataTypes::send_time, JsonValue::String(now_ms.to_string()))
.add_data(
DataTypes::sender_id,
JsonValue::Number(Number::from(sender)),
)
.add_data(
DataTypes::content,
JsonValue::String(original.get_data(DataTypes::content).unwrap().to_string()),
)
}
}

View file

@ -1,2 +1 @@
pub mod communication;
pub mod user;

View file

@ -5,24 +5,17 @@ mod omega;
mod rho;
mod util;
use async_tungstenite::accept_hdr_async;
use std::env;
use dotenv::dotenv;
use futures::StreamExt;
use once_cell::sync::Lazy;
use std::{env, sync::Arc};
use tokio::net::TcpListener;
use tokio_util::compat::TokioAsyncReadCompatExt;
use tungstenite::handshake::server::{Request, Response};
use crate::{
anonymous_clients::{
anonymous_client_connection::AnonymousClientConnection, anonymous_manager,
},
calls::call_util::garbage_collect_calls,
rho::{client_connection::ClientConnection, iota_connection::IotaConnection},
rho::server::start,
util::{
crypto_helper::{load_public_key, load_secret_key},
logger::{PrintType, startup},
logger::startup,
},
};
@ -39,191 +32,8 @@ pub fn get_public_key() -> x448::PublicKey {
async fn main() {
dotenv().ok();
startup();
let address = format!(
"{}:{}",
env::var("IP").unwrap_or("0.0.0.0".to_string()),
env::var("PORT").unwrap_or("959".to_string())
);
let listener = TcpListener::bind(&address).await.unwrap();
log!(
0,
PrintType::General,
"WebSocket server listening on {}",
address,
);
start(959).await;
garbage_collect_calls();
while let Ok((stream, _)) = listener.accept().await {
tokio::spawn(async move {
let mut path: String = "/".to_string();
let callback = |req: &Request, response: Response| {
path = req.uri().path().to_string();
Ok(response)
};
let ws_stream = match accept_hdr_async(stream.compat(), callback).await {
Ok(ws) => ws,
Err(e) => {
log!(0, PrintType::General, "WebSocket upgrade failed: {}", e,);
return;
}
};
let (sender, receiver) = ws_stream.split();
if path.starts_with("/ws/client") {
log_in!(0, PrintType::Client, "New Client connection");
let client_conn: Arc<ClientConnection> =
Arc::from(ClientConnection::new(sender, receiver));
loop {
let msg_result = {
let mut session_lock = client_conn.receiver.write().await;
session_lock.next().await
};
match msg_result {
Some(Ok(msg)) => {
if msg.is_text() {
let text = msg.into_text().unwrap();
client_conn.clone().handle_message(text).await;
} else if msg.is_close() {
log_in!(
client_conn.get_user_id().await,
PrintType::Client,
"Client disconnected"
);
client_conn.handle_close().await;
return;
}
}
Some(Err(e)) => {
log_err!(
client_conn.get_user_id().await,
PrintType::Client,
"WebSocket error: {}",
e
);
client_conn.handle_close().await;
return;
}
_ => {
log_in!(
client_conn.get_user_id().await,
PrintType::Client,
"Client stream ended"
);
client_conn.handle_close().await;
return;
}
}
}
} else if path.starts_with("/ws/anonymous_client") {
log_in!(0, PrintType::Client, "New Anonymous Client connection");
let client_conn: Arc<AnonymousClientConnection> =
Arc::from(AnonymousClientConnection::new(sender, receiver));
anonymous_manager::add_anonymous_user(client_conn.clone()).await;
loop {
let msg_result = {
let mut session_lock = client_conn.receiver.write().await;
session_lock.next().await
};
match msg_result {
Some(Ok(msg)) => {
if msg.is_text() {
let text = msg.into_text().unwrap();
client_conn.clone().handle_message(text).await;
} else if msg.is_close() {
log_in!(
client_conn.get_user_id().await,
PrintType::Client,
"Anonymous Client disconnected"
);
anonymous_manager::remove_anonymous_user(
client_conn.get_user_id().await,
)
.await;
client_conn.handle_close().await;
return;
}
}
Some(Err(e)) => {
log_err!(
client_conn.get_user_id().await,
PrintType::Client,
"WebSocket error: {}",
e
);
anonymous_manager::remove_anonymous_user(
client_conn.get_user_id().await,
)
.await;
client_conn.handle_close().await;
return;
}
_ => {
log_in!(
client_conn.get_user_id().await,
PrintType::Client,
"Anonymous Client stream ended"
);
anonymous_manager::remove_anonymous_user(
client_conn.get_user_id().await,
)
.await;
client_conn.handle_close().await;
return;
}
}
}
} else if path.starts_with("/ws/iota") {
log_in!(0, PrintType::Iota, "New Iota connection");
let iota_conn: Arc<IotaConnection> =
Arc::from(IotaConnection::new(sender, receiver));
loop {
let msg_result = {
let mut session_lock = iota_conn.receiver.write().await;
session_lock.next().await
};
match msg_result {
Some(Ok(msg)) => {
if msg.is_text() {
let text = msg.into_text().unwrap();
iota_conn.clone().handle_message(text).await;
} else if msg.is_close() {
log_in!(
iota_conn.get_iota_id().await,
PrintType::Iota,
"Iota disconnected"
);
iota_conn.handle_close().await;
return;
}
}
Some(Err(e)) => {
log_err!(
iota_conn.get_iota_id().await,
PrintType::Iota,
"WebSocket error: {}",
e
);
iota_conn.handle_close().await;
return;
}
_ => {
// Stream ended
log_in!(
iota_conn.get_iota_id().await,
PrintType::Iota,
"Iota stream ended"
);
iota_conn.handle_close().await;
return;
}
}
}
}
});
}
}

View file

@ -1,2 +1 @@
pub mod omega_connection;
pub mod ping_pong_task;

View file

@ -1,392 +1,165 @@
use async_tungstenite::{
WebSocketReceiver, WebSocketSender,
stream::Stream,
tokio::{TokioAdapter, connect_async},
tungstenite::protocol::Message,
use std::{
sync::Arc,
time::{Duration, Instant},
};
use crossterm::style::Print;
use dashmap::DashMap;
use futures::prelude::*;
use json::{JsonValue, number::Number};
use once_cell::sync::Lazy;
use std::{collections::HashMap, env, sync::Arc, time::Duration};
use tokio::{
net::TcpStream,
sync::{Mutex, RwLock, mpsc},
time::{Instant, sleep},
};
use tokio_native_tls::TlsStream;
use uuid::Uuid;
use tokio::sync::{Mutex, RwLock, mpsc};
use crate::{
data::{
communication::{CommunicationType, CommunicationValue, DataTypes},
user::UserStatus,
},
get_private_key, log, log_cv_out, log_in, log_out,
rho::rho_manager::{self, RHO_CONNECTIONS, connection_count},
util::{
crypto_helper::{decrypt_b64, secret_key_to_base64},
logger::PrintType,
},
};
use crate::{log_cv_in, log_err};
use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue};
use epsilon_native::{Receiver, Sender, connect};
pub struct WaitingTask {
pub task: Box<dyn Fn(Arc<OmegaConnection>, CommunicationValue) -> bool + Send + Sync>,
pub inserted_at: Instant,
}
use crate::{data::user::UserStatus, rho::rho_manager};
pub static WAITING_TASKS: Lazy<DashMap<Uuid, WaitingTask>> = Lazy::new(DashMap::new);
static WAITING: Lazy<
DashMap<
u32,
(
Instant,
Box<dyn Fn(Arc<OmegaConnection>, CommunicationValue) -> bool + Send + Sync>,
),
>,
> = Lazy::new(DashMap::new);
static OMEGA_CONNECTION: Lazy<Arc<OmegaConnection>> = Lazy::new(|| {
let conn = Arc::new(OmegaConnection::new());
let conn_clone = conn.clone();
tokio::spawn(async move {
conn_clone.connect_internal(0).await;
});
static OMEGA_CONNECTION: Lazy<Arc<OmegaConnection>> = Lazy::new(|| OmegaConnection::new());
tokio::spawn(async {
loop {
sleep(Duration::from_secs(60)).await;
WAITING_TASKS.retain(|_, v| v.inserted_at.elapsed() < Duration::from_secs(60));
}
});
conn
});
#[allow(dead_code)]
pub fn get_omega_connection() -> Arc<OmegaConnection> {
OMEGA_CONNECTION.clone()
}
#[derive(Clone)]
pub struct OmegaConnection {
write: Arc<
Mutex<
Option<
WebSocketSender<
Stream<TokioAdapter<TcpStream>, TokioAdapter<TlsStream<TcpStream>>>,
>,
>,
>,
>,
read: Arc<
Mutex<
Option<
WebSocketReceiver<
Stream<TokioAdapter<TcpStream>, TokioAdapter<TlsStream<TcpStream>>>,
>,
>,
>,
>,
pingpong: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>,
pub last_ping: Arc<Mutex<i64>>,
pub message_send_times: Arc<Mutex<HashMap<Uuid, Instant>>>,
state: Arc<RwLock<ConnectionState>>,
}
#[derive(Clone, PartialEq, Eq)]
enum ConnectionState {
#[derive(Clone, PartialEq)]
enum State {
Disconnected,
Connecting,
Connected,
}
pub struct OmegaConnection {
sender: Arc<Mutex<Option<Sender>>>,
receiver: Arc<Mutex<Option<Receiver>>>,
state: Arc<RwLock<State>>,
}
impl OmegaConnection {
pub fn new() -> Self {
OmegaConnection {
read: Arc::new(Mutex::new(None)),
write: Arc::new(Mutex::new(None)),
pingpong: Arc::new(Mutex::new(None)),
last_ping: Arc::new(Mutex::new(-1)),
message_send_times: Arc::new(Mutex::new(HashMap::new())),
state: Arc::new(RwLock::new(ConnectionState::Disconnected)),
}
pub fn new() -> Arc<Self> {
Arc::new(Self {
sender: Arc::new(Mutex::new(None)),
receiver: Arc::new(Mutex::new(None)),
state: Arc::new(RwLock::new(State::Disconnected)),
})
}
async fn connect_internal(self: Arc<OmegaConnection>, mut retry: usize) {
if self.state.read().await.eq(&ConnectionState::Connected) {
return;
}
if self.state.read().await.eq(&ConnectionState::Connecting) {
let start = Instant::now();
let timeout = Duration::from_secs(10);
while self.state.read().await.eq(&ConnectionState::Connecting)
&& start.elapsed() < timeout
{
sleep(Duration::from_millis(100)).await;
}
return;
}
*self.state.write().await = ConnectionState::Connecting;
pub async fn connect(self: Arc<Self>, addr: &str) -> Result<(), String> {
*self.state.write().await = State::Connecting;
if let Some(handle) = self.pingpong.lock().await.take() {
handle.abort();
}
let (sender, receiver) = connect(addr)
.await
.map_err(|e| format!("Connect error: {e:?}"))?;
loop {
if retry > 500 {
log_err!(
0,
PrintType::Omega,
"Max retry attempts reached, giving up."
);
*self.state.write().await = ConnectionState::Disconnected;
return;
}
*self.sender.lock().await = Some(sender);
*self.receiver.lock().await = Some(receiver);
let url_str =
env::var("OMEGA_HOST").unwrap_or("wss://omega.tensamin.net/ws/omikron".to_string());
match connect_async(&url_str).await {
Ok((ws_stream, _)) => {
log_in!(0, PrintType::Omega, "WebSocket connected to {}", url_str);
retry = 0;
let (write, read) = ws_stream.split();
*self.read.lock().await = Some(read);
*self.write.lock().await = Some(write);
*self.state.write().await = State::Connected;
*self.state.write().await = ConnectionState::Connected;
let read_self = self.clone();
tokio::spawn(async move {
read_self.read_loop().await;
});
let read_loop_self = self.clone();
let read_loop_handle = tokio::spawn(async move {
read_loop_self.read_loop().await;
});
self.identify().await?;
let cloned_self = self.clone();
tokio::spawn(async move {
let id = Uuid::new_v4();
Ok(())
}
let identify_msg =
CommunicationValue::new(CommunicationType::identification)
.with_id(id)
.add_data(
DataTypes::omikron,
JsonValue::Number(Number::from(
env::var("ID")
.unwrap_or("0".to_string())
.parse::<i64>()
.unwrap_or(0),
)),
);
WAITING_TASKS.insert(
id,
WaitingTask {
task: Box::new(|selfc, cv| {
if cv.is_type(CommunicationType::error_not_found) {
log_err!(0,
PrintType::Omega,
"Identification failed: Omikron ID not found on Omega.",
);
return false;
}
if !cv.is_type(CommunicationType::challenge) {
return false;
}
tokio::spawn(async move {
let task = async move {
let challenge = cv
.get_data(DataTypes::challenge)
.and_then(|v| v.as_str())
.ok_or_else(|| {
"Challenge not found or not a string".to_string()
})?;
async fn identify(&self) -> Result<(), String> {
let msg = CommunicationValue::new(CommunicationType::identification)
.add_data(DataTypes::omikron, DataValue::Number(1));
let server_pub_key = cv
.get_data(DataTypes::public_key)
.and_then(|v| v.as_str())
.ok_or_else(|| {
"Public key from server not found or not a string"
.to_string()
})?;
self.await_response(&msg, Some(Duration::from_secs(10)))
.await?;
let decrypted_challenge = decrypt_b64(
&secret_key_to_base64(&get_private_key()),
server_pub_key,
challenge,
)
.map_err(|e| {
format!("Failed to decrypt challenge: {:?}", e)
})?;
let response_msg = CommunicationValue::new(
CommunicationType::challenge_response,
)
.with_id(cv.get_id())
.add_data(
DataTypes::challenge,
JsonValue::String(decrypted_challenge),
);
let response_id = response_msg.get_id();
WAITING_TASKS.insert(
response_id,
WaitingTask {
task: Box::new(|selfc, final_cv| {
if !final_cv
.is_type(CommunicationType::identification_response)
{
log_err!(0,
PrintType::Omega,
"Expected identification_response, got something else.",
);
return false;
}
if let Some(accepted) = final_cv.get_data(DataTypes::accepted).and_then(|v| v.as_bool()) {
if !accepted {
log_err!(0, PrintType::Omega, "Omega did not accept identification.");
return false;
}
} else {
log_err!(0, PrintType::Omega, "Omega response did not contain 'accepted' field.");
return false;
}
tokio::spawn(async move {
let mut connected_iota_ids: Vec<JsonValue> = Vec::new();
let mut connected_user_ids: Vec<JsonValue> = Vec::new();
let rho_connections_reader = RHO_CONNECTIONS.read().await;
for iota_id in rho_connections_reader.keys() {
connected_iota_ids.push(JsonValue::from(*iota_id));
}
for rho in rho_connections_reader.values() {
for client_conn in rho.get_client_connections().await {
connected_user_ids.push(JsonValue::from(client_conn.get_user_id().await));
}
}
drop(rho_connections_reader);
let sync_msg = CommunicationValue::new(CommunicationType::sync_client_iota_status)
.add_data(DataTypes::iota_ids, JsonValue::Array(connected_iota_ids))
.add_data(DataTypes::user_ids, JsonValue::Array(connected_user_ids))
.add_data(DataTypes::rho_connections, JsonValue::from(connection_count().await));
selfc.send_message(&sync_msg).await;
});
log!(0,
PrintType::Omega,
"Successfully identified with Omega.",
);
true
}),
inserted_at: Instant::now(),
}
);
selfc.send_message(&response_msg).await;
Ok::<(), String>(())
};
if let Err(e) = task.await {
log_err!(0, PrintType::Omega, "{}", &e);
}
});
true
}),
inserted_at: Instant::now(),
}
);
cloned_self.send_message(&identify_msg).await
});
let ping_pong_self = self.clone();
let handle = tokio::spawn(async move {
loop {
if *ping_pong_self.state.read().await != ConnectionState::Connected {
break;
}
ping_pong_self.send_ping().await;
sleep(Duration::from_secs(5)).await;
}
});
*self.pingpong.lock().await = Some(handle);
read_loop_handle.await.unwrap_or_else(|e| {
log_err!(0, PrintType::Omega, "Read loop task failed: {}", e)
});
*self.read.lock().await = None;
*self.write.lock().await = None;
*self.state.write().await = ConnectionState::Disconnected;
log_err!(0, PrintType::Omega, "Connection lost. Retrying...");
retry += 1;
sleep(Duration::from_secs(2)).await;
}
Err(e) => {
*self.state.write().await = ConnectionState::Disconnected;
log_err!(
0,
PrintType::Omega,
"WebSocket connection failed (attempt {}): {}",
retry + 1,
e,
);
retry += 1;
sleep(Duration::from_secs(2)).await;
continue;
}
}
}
Ok(())
}
async fn read_loop(self: Arc<Self>) {
let mut reader = match self.read.lock().await.take() {
Some(reader) => reader,
None => return,
};
loop {
let msg = reader.next().await;
match msg {
Some(Ok(Message::Text(msg))) => {
let cv = CommunicationValue::from_json(&msg);
log_cv_in!(PrintType::Omega, cv);
if cv.is_type(CommunicationType::pong) || cv.is_type(CommunicationType::ping) {
self.handle_pong(&cv, true).await;
continue;
}
let msg_id = cv.get_id();
if let Some(task) = WAITING_TASKS.remove(&msg_id) {
if (task.1.task)(self.clone(), cv.clone()) {
continue;
}
} else {
}
let result = {
let mut guard = self.receiver.lock().await;
match guard.as_mut() {
Some(receiver) => receiver.receive().await,
None => return,
}
#[allow(non_snake_case)]
Some(Ok(Message::Close(_))) | None => break,
Some(Err(_)) => break,
_ => {}
};
let cv = match result {
Ok(v) => v,
Err(_) => {
*self.state.write().await = State::Disconnected;
return;
}
};
let id = cv.get_id();
if let Some((_, task)) = WAITING.remove(&id) {
(task.1)(self.clone(), cv);
continue;
}
if cv.is_type(CommunicationType::ping) {
let pong = CommunicationValue::new(CommunicationType::pong).with_id(id);
let _ = self.send(&pong).await;
}
}
}
pub async fn send_message(&self, cv: &CommunicationValue) {
log_cv_out!(PrintType::Omega, cv);
let msg = cv.to_json().to_string();
let mut guard = self.write.lock().await;
if let Some(ws) = guard.as_mut() {
let _ = ws.send(Message::Text(msg.into())).await;
pub async fn send(&self, cv: &CommunicationValue) -> Result<(), String> {
let guard = self.sender.lock().await;
if let Some(sender) = guard.as_ref() {
sender
.send(cv)
.await
.map_err(|e| format!("Send error: {e:?}"))
} else {
Err("Not connected".into())
}
}
pub async fn await_response(
&self,
cv: &CommunicationValue,
timeout: Option<Duration>,
) -> Result<CommunicationValue, String> {
let (tx, mut rx) = mpsc::channel(1);
let id = cv.get_id();
WAITING.insert(
id.into(),
(
Instant::now(),
Box::new(move |_, response| {
let _ = tx.try_send(response);
true
}),
),
);
self.send(cv).await?;
match tokio::time::timeout(timeout.unwrap_or(Duration::from_secs(10)), rx.recv()).await {
Ok(Some(v)) => Ok(v),
_ => {
WAITING.remove(&id.into());
Err("Timeout waiting for response".into())
}
}
}
pub async fn close_iota(iota_id: i64) {
let cv = CommunicationValue::new(CommunicationType::iota_disconnected)
.add_data(DataTypes::iota_id, JsonValue::from(iota_id));
.add_data(DataTypes::iota_id, DataValue::Number(iota_id));
OmegaConnection::send_global(cv).await;
}
pub async fn client_changed(_iota_id: i64, user_id: i64, state: UserStatus) {
let msg_type = match state {
UserStatus::iota_offline => Some(CommunicationType::user_disconnected),
@ -396,25 +169,28 @@ impl OmegaConnection {
if let Some(t) = msg_type {
let cv =
CommunicationValue::new(t).add_data(DataTypes::user_id, JsonValue::from(user_id));
CommunicationValue::new(t).add_data(DataTypes::user_id, DataValue::Number(user_id));
OmegaConnection::send_global(cv).await;
}
}
async fn send_global(cv: CommunicationValue) {
OMEGA_CONNECTION.send(&cv).await;
}
pub async fn user_states(user_id: i64, user_ids: Vec<i64>) {
let user_ids_str = user_ids
.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(",");
.map(|id| DataValue::Number(*id))
.collect::<Vec<_>>();
let cv = CommunicationValue::new(CommunicationType::get_states)
.add_data(DataTypes::user_ids, JsonValue::from(user_ids_str));
.add_data(DataTypes::user_ids, DataValue::Array(user_ids_str));
let msg_id = cv.get_id();
WAITING_TASKS.insert(
WAITING.insert(
msg_id,
WaitingTask {
task: Box::new(
(
Instant::now(),
Box::new(
move |_: Arc<OmegaConnection>, response: CommunicationValue| {
tokio::spawn(async move {
let rho = rho_manager::get_rho_con_for_user(user_id).await;
@ -427,84 +203,9 @@ impl OmegaConnection {
true
},
),
inserted_at: Instant::now(),
},
),
);
OmegaConnection::send_global(cv).await;
}
async fn send_global(cv: CommunicationValue) {
OMEGA_CONNECTION.send_message(&cv).await;
}
pub async fn await_connection(&self, timeout_duration: Option<Duration>) -> Result<(), String> {
if *self.state.read().await == ConnectionState::Connected {
return Ok(());
}
let timeout = timeout_duration.unwrap_or(Duration::from_secs(10));
let start = Instant::now();
loop {
if *self.state.read().await == ConnectionState::Connected {
return Ok(());
}
if start.elapsed() >= timeout {
return Err(format!(
"Connection not established within {} seconds",
timeout.as_secs()
));
}
sleep(Duration::from_millis(100)).await; // short interval polling
}
}
pub async fn await_response(
&self,
cv: &CommunicationValue,
timeout_duration: Option<Duration>,
) -> Result<CommunicationValue, String> {
self.await_connection(timeout_duration).await?;
let (tx, mut rx) = mpsc::channel(1);
let msg_id = cv.get_id();
let task_tx = tx.clone();
WAITING_TASKS.insert(
msg_id,
WaitingTask {
task: Box::new(move |_, response_cv| {
let inner_tx = task_tx.clone();
tokio::spawn(async move {
if inner_tx.send(response_cv).await.is_err() {
log_err!(
0,
PrintType::Omega,
"Failed to send response back to awaiter",
);
}
});
true
}),
inserted_at: Instant::now(),
},
);
self.send_message(cv).await;
let timeout = timeout_duration.unwrap_or(Duration::from_secs(10));
match tokio::time::timeout(timeout, rx.recv()).await {
Ok(Some(response_cv)) => Ok(response_cv),
Ok(_) => Err("Failed to receive response, channel was closed.".to_string()),
Err(_) => {
WAITING_TASKS.remove(&msg_id);
Err(format!(
"Request timed out after {} seconds.",
timeout.as_secs()
))
}
}
}
}

View file

@ -1,30 +1,29 @@
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
use crate::omega::omega_connection::OmegaConnection;
use json::number::Number;
use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue, rand_u32};
use std::time::Duration;
use tokio::time::Instant;
use uuid::Uuid;
use crate::omega::omega_connection::OmegaConnection;
const PING_TIMEOUT: Duration = Duration::from_secs(30);
impl OmegaConnection {
pub async fn send_ping(&self) {
let uuid = Uuid::new_v4();
let id = rand_u32();
let send_time = Instant::now();
let mut message_send_times = self.message_send_times.lock().await;
message_send_times.retain(|_uuid, time| time.elapsed() < PING_TIMEOUT);
message_send_times.insert(uuid, send_time);
message_send_times.insert(id as i64, send_time);
self.send_ping_message(uuid).await;
self.send_ping_message(id).await;
}
pub async fn send_ping_message(&self, uuid: Uuid) {
pub async fn send_ping_message(&self, id: u32) {
let ping_message = CommunicationValue::new(CommunicationType::ping)
.with_id(uuid)
.add_data_num(
.with_id(id)
.add_data(
DataTypes::last_ping,
Number::from(*self.last_ping.lock().await),
DataValue::Number(self.last_ping.lock().await.unwrap()),
);
self.send_message(&ping_message).await;
@ -34,7 +33,7 @@ impl OmegaConnection {
pub async fn handle_pong(&self, cv: &CommunicationValue, _log: bool) {
let id = cv.get_id();
let mut message_send_times = self.message_send_times.lock().await;
if let Some(send_time) = message_send_times.remove(&id) {
if let Some(send_time) = message_send_times.remove(&(id as i64)) {
let ping = Instant::now().duration_since(send_time).as_millis() as i64;
*self.last_ping.lock().await = ping;
}

View file

@ -1,43 +1,24 @@
use async_tungstenite::tungstenite::Message;
use async_tungstenite::{WebSocketReceiver, WebSocketSender};
use base64::Engine;
use base64::engine::general_purpose::STANDARD;
use json::JsonValue;
use json::number::Number;
use rand::Rng;
use rand::distributions::Alphanumeric;
use std::str::FromStr;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use sysinfo::System;
use tokio::sync::RwLock;
use tokio_util::compat::Compat;
use tungstenite::Utf8Bytes;
use uuid::Uuid;
use super::{rho_connection::RhoConnection, rho_manager};
use crate::anonymous_clients::anonymous_manager;
use crate::calls::{call_manager, call_util};
use crate::omega::omega_connection::get_omega_connection;
use crate::util::crypto_helper::{load_public_key, public_key_to_base64};
use crate::util::crypto_util::{DataFormat, SecurePayload};
use crate::rho::connection::GeneralConnection;
use crate::rho::{rho_connection::RhoConnection, rho_manager};
use crate::util::logger::PrintType;
use crate::{
data::{
communication::{CommunicationType, CommunicationValue, DataTypes},
user::UserStatus,
},
omega::omega_connection::OmegaConnection,
};
use crate::{get_private_key, get_public_key, log_cv_in, log_in, log_out};
use crate::{data::user::UserStatus, omega::omega_connection::OmegaConnection};
use crate::{log_cv_in, log_cv_out, log_out};
use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue};
use epsilon_native::{Receiver, Sender};
use std::str::FromStr;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::sync::RwLock;
use uuid::Uuid;
pub struct ClientConnection {
pub sender: Arc<RwLock<WebSocketSender<Compat<tokio::net::TcpStream>>>>,
pub receiver: Arc<RwLock<WebSocketReceiver<Compat<tokio::net::TcpStream>>>>,
pub user_id: Arc<RwLock<i64>>,
identified: Arc<RwLock<bool>>,
challenged: Arc<RwLock<bool>>,
challenge: Arc<RwLock<String>>,
pub user_id: u64,
pub sender: Arc<Sender>,
pub receiver: Arc<Receiver>,
pub ping: Arc<RwLock<i64>>,
pub_key: Arc<RwLock<Option<Vec<u8>>>>,
pub rho_connection: Arc<RwLock<Option<Arc<RhoConnection>>>>,
@ -46,34 +27,30 @@ pub struct ClientConnection {
}
impl ClientConnection {
/// Create a new ClientConnection
pub fn new(
sender: WebSocketSender<Compat<tokio::net::TcpStream>>,
receiver: WebSocketReceiver<Compat<tokio::net::TcpStream>>,
) -> Arc<Self> {
pub async fn from_general(general: Arc<GeneralConnection>, user_id: u64) -> Arc<Self> {
Arc::new(Self {
sender: Arc::new(RwLock::new(sender)),
receiver: Arc::new(RwLock::new(receiver)),
user_id: Arc::new(RwLock::new(0)),
identified: Arc::new(RwLock::new(false)),
challenged: Arc::new(RwLock::new(false)),
challenge: Arc::new(RwLock::new(String::new())),
ping: Arc::new(RwLock::new(-1)),
ping: Arc::new(RwLock::new(0)),
pub_key: Arc::new(RwLock::new(None)),
rho_connection: Arc::new(RwLock::new(None)),
interested_users: Arc::new(RwLock::new(Vec::new())),
is_open: Arc::new(RwLock::new(true)),
sender: general.sender.clone(),
receiver: general.receiver.clone(),
user_id: user_id,
})
}
pub fn start(self: Arc<Self>) {
let self_clone = self.clone();
tokio::spawn(async move {
while let Ok(cv) = self_clone.receiver.receive().await {
self_clone.clone().handle_message(cv).await;
}
});
}
/// Get the user ID
pub async fn get_user_id(&self) -> i64 {
*self.user_id.read().await
}
/// Check if connection is identified
pub async fn is_identified(&self) -> bool {
*self.identified.read().await
pub async fn get_user_id(&self) -> u64 {
self.user_id
}
/// Get current ping
@ -86,211 +63,30 @@ impl ClientConnection {
self.rho_connection.read().await.clone()
}
/// Send a string message to the client
pub async fn send_message_str(self: Arc<Self>, message: &str) {
let mut session = self.sender.write().await;
if let Err(e) = session
.send(Message::Text(Utf8Bytes::from(message.to_string())))
.await
{
log_out!(
self.get_user_id().await,
PrintType::Client,
"Failed to send message to client: {}",
e,
);
}
}
/// Send a CommunicationValue to the client
pub async fn send_message(self: Arc<Self>, cv: &CommunicationValue) {
if !*self.is_open.read().await {
log_out!(
self.get_user_id().await,
self.user_id as i64,
PrintType::Client,
"Attempted to send message to a closed connection."
);
return;
}
if !cv.is_type(CommunicationType::pong) && !cv.is_type(CommunicationType::ping) {
log_out!(
self.get_user_id().await,
PrintType::Client,
"{}",
&cv.to_json().to_string()
);
log_cv_out!(PrintType::Client, &cv);
}
self.send_message_str(&cv.to_json().to_string()).await;
self.sender.send(&cv).await;
}
/// Handle incoming message from client
pub async fn handle_message(self: Arc<Self>, message: Utf8Bytes) {
pub async fn handle_message(self: Arc<Self>, cv: CommunicationValue) {
tokio::spawn(async move {
let cv = CommunicationValue::from_json(&message);
if cv.is_type(CommunicationType::ping) {
self.handle_ping(cv).await;
return;
}
log_cv_in!(PrintType::Client, cv);
let identified = *self.identified.read().await;
let challenged = *self.challenged.read().await;
// Handle identification
if !identified && cv.is_type(CommunicationType::identification) {
let user_id = cv
.get_data(DataTypes::user_id)
.and_then(|v| v.as_i64())
.unwrap_or(0);
if user_id == 0 {
log_out!(
self.get_user_id().await,
PrintType::Client,
"Invalid USER ID"
);
self.clone()
.send_error_response(&cv.get_id(), CommunicationType::error_invalid_data)
.await;
self.close().await;
return;
}
*self.user_id.write().await = user_id;
let get_pub_key_msg = CommunicationValue::new(CommunicationType::get_user_data)
.with_id(cv.get_id())
.add_data(DataTypes::user_id, JsonValue::from(user_id));
let response_cv = get_omega_connection()
.await_response(&get_pub_key_msg, Some(Duration::from_secs(20)))
.await;
if let Ok(response_cv) = response_cv {
if !response_cv.is_type(CommunicationType::get_user_data) {
self.clone()
.send_error_response(&cv.get_id(), CommunicationType::error_internal)
.await;
self.close().await;
return;
}
let base64_pub = response_cv
.get_data(DataTypes::public_key)
.and_then(|v| v.as_str())
.unwrap_or("");
let pub_key = match load_public_key(base64_pub) {
Some(pk) => pk,
_ => {
self.clone()
.send_error_response(
&cv.get_id(),
CommunicationType::error_invalid_public_key,
)
.await;
self.close().await;
return;
}
};
*self.pub_key.write().await = Some(pub_key.as_bytes().to_vec());
let challenge: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect();
*self.challenge.write().await = challenge.clone();
let encrypted_challenge =
SecurePayload::new(&challenge, DataFormat::Raw, get_private_key())
.unwrap()
.encrypt_x448(pub_key)
.unwrap()
.export(DataFormat::Base64);
*self.identified.write().await = true;
let challenge_msg = CommunicationValue::new(CommunicationType::challenge)
.with_id(cv.get_id())
.add_data_str(
DataTypes::public_key,
public_key_to_base64(&get_public_key()),
)
.add_data_str(DataTypes::challenge, encrypted_challenge);
self.send_message(&challenge_msg).await;
} else {
self.clone()
.send_error_response(&cv.get_id(), CommunicationType::error_internal)
.await;
self.close().await;
return;
}
return;
}
if identified && !challenged && cv.is_type(CommunicationType::challenge_response) {
let client_response = cv
.get_data(DataTypes::challenge)
.and_then(|v| v.as_str())
.unwrap_or("");
let debase64d = STANDARD.decode(&client_response).unwrap();
if String::from_utf8(debase64d.clone()).unwrap() == *self.challenge.read().await {
*self.challenged.write().await = true;
let user_id = self.get_user_id().await;
let rho_connection = match rho_manager::get_rho_con_for_user(user_id).await {
Some(rho) => rho,
_ => {
self.send_error_response(
&cv.get_id(),
CommunicationType::error_no_iota,
)
.await;
return;
}
};
rho_connection.add_client_connection(self.clone()).await;
// Set identification data
{
let mut user_id_guard = self.user_id.write().await;
*user_id_guard = user_id;
}
{
let mut identified_guard = self.identified.write().await;
*identified_guard = true;
}
*self.rho_connection.write().await = Some(Arc::clone(&rho_connection));
let response =
CommunicationValue::new(CommunicationType::identification_response)
.with_id(cv.get_id());
self.send_message(&response).await;
} else {
self.clone()
.send_error_response(
&cv.get_id(),
CommunicationType::error_not_authenticated,
)
.await;
self.close().await;
return;
}
return;
}
if !self.is_identified().await {
self.clone()
.send_error_response(&cv.get_id(), CommunicationType::error_not_authenticated)
.await;
self.close().await;
return;
}
// Handle client status changes
if cv.is_type(CommunicationType::client_changed) {
@ -326,17 +122,9 @@ impl ClientConnection {
}
if cv.is_type(CommunicationType::get_user_data) {
if let Some(anonymous) = {
if let Some(user_id) = cv
.get_data(DataTypes::user_id)
.unwrap_or(&JsonValue::Null)
.as_i64()
{
anonymous_manager::get_anonymous_user(user_id).await
} else if let Some(username) = cv
.get_data(DataTypes::username)
.unwrap_or(&JsonValue::Null)
.as_str()
{
if let Some(user_id) = cv.get_data(DataTypes::user_id).as_number() {
anonymous_manager::get_anonymous_user(user_id as u64).await
} else if let Some(username) = cv.get_data(DataTypes::username).as_str() {
anonymous_manager::get_anonymous_user_by_name(username.to_string()).await
} else {
None
@ -344,14 +132,23 @@ impl ClientConnection {
} {
let response = CommunicationValue::new(CommunicationType::get_user_data)
.with_id(cv.get_id())
.add_data_str(DataTypes::username, anonymous.get_user_name().await)
.add_data(
DataTypes::username,
DataValue::Str(anonymous.get_user_name().await),
)
.add_data(
DataTypes::user_id,
JsonValue::Number(Number::from(anonymous.get_user_id().await)),
DataValue::Number(anonymous.get_user_id() as i64),
)
.add_data_str(DataTypes::display, anonymous.get_display_name().await)
.add_data_str(DataTypes::user_state, "online".to_string())
.add_data_str(DataTypes::avatar, anonymous.get_avatar().await);
.add_data(
DataTypes::display,
DataValue::Str(anonymous.get_display_name().await),
)
.add_data(
DataTypes::avatar,
DataValue::Str(anonymous.get_avatar().await),
)
.add_data(DataTypes::user_state, DataValue::Str("online".to_string()));
self.send_message(&response).await;
@ -367,7 +164,8 @@ impl ClientConnection {
|| cv.is_type(CommunicationType::delete_user)
{
let sender = self.get_user_id().await;
self.handle_omega_forward(cv.with_sender(sender)).await;
self.handle_omega_forward(cv.with_sender(sender as u64))
.await;
return;
}
// Forward other messages to Iota
@ -378,10 +176,7 @@ impl ClientConnection {
let client_for_closure = self.clone();
tokio::spawn(async move {
let response_cv = get_omega_connection()
.await_response(
&cv.with_sender(*self.user_id.read().await),
Some(Duration::from_secs(20)),
)
.await_response(&cv.with_sender(self.user_id), Some(Duration::from_secs(20)))
.await;
if let Ok(response_cv) = response_cv {
client_for_closure.send_message(&response_cv).await;
@ -392,13 +187,13 @@ impl ClientConnection {
/// Handle ping message
async fn handle_ping(self: Arc<Self>, cv: CommunicationValue) {
// Update our ping if provided
if let Some(last_ping) = cv.get_data(DataTypes::last_ping) {
if let DataValue::Number(last_ping) = cv.get_data(DataTypes::last_ping) {
let current = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
let mut ping_guard = self.ping.write().await;
*ping_guard = current as i64 - last_ping.as_i64().unwrap();
*ping_guard = current as i64 - last_ping;
}
// Get Iota ping from RhoConnection
@ -411,7 +206,7 @@ impl ClientConnection {
// Send pong response
let response = CommunicationValue::new(CommunicationType::pong)
.with_id(cv.get_id())
.add_data(DataTypes::ping_iota, JsonValue::from(iota_ping));
.add_data(DataTypes::ping_iota, DataValue::Number(iota_ping));
self.send_message(&response).await;
}
@ -419,51 +214,47 @@ impl ClientConnection {
/// Handle client status change
async fn handle_client_changed(self: Arc<Self>, cv: CommunicationValue) {
let user_id = self.get_user_id().await;
if let Some(_status_str) = cv.get_data(DataTypes::user_state) {
if let DataValue::Str(_status_str) = cv.get_data(DataTypes::user_state) {
let user_status = UserStatus::user_online;
if let Some(rho_conn) = self.get_rho_connection().await {
OmegaConnection::client_changed(rho_conn.get_iota_id().await, user_id, user_status)
.await;
OmegaConnection::client_changed(
rho_conn.get_iota_id().await as i64,
user_id as i64,
user_status,
)
.await;
}
}
}
/// Handle call invite
async fn handle_call_invite(self: Arc<Self>, cv: CommunicationValue) {
let receiver_id: i64 = cv
.get_data(DataTypes::receiver_id)
.unwrap_or(&json::JsonValue::Number(Number::from(0)))
.as_i64()
.unwrap_or(0);
let receiver_id: i64 = cv.get_data(DataTypes::receiver_id).as_number().unwrap_or(0);
if receiver_id == 0 {
self.send_error_response(&cv.get_id(), CommunicationType::error_no_user_id)
self.send_error_response(cv.get_id(), CommunicationType::error_no_user_id)
.await;
return;
}
let call_id = match cv.get_data(DataTypes::call_id) {
Some(id_str) => match Uuid::parse_str(&id_str.to_string()) {
DataValue::Str(id_str) => match Uuid::parse_str(id_str.as_str()) {
Ok(id) => id,
Err(_) => {
self.send_error_response(
&cv.get_id(),
CommunicationType::error_invalid_call_id,
)
.await;
self.send_error_response(cv.get_id(), CommunicationType::error_invalid_call_id)
.await;
return;
}
},
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error_no_call_id)
self.send_error_response(cv.get_id(), CommunicationType::error_no_call_id)
.await;
return;
}
};
let invited =
call_manager::add_invite(call_id, *self.user_id.read().await, receiver_id).await;
let invited = call_manager::add_invite(call_id, self.user_id, receiver_id as u64).await;
if !invited {
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_call_id)
self.send_error_response(cv.get_id(), CommunicationType::error_invalid_call_id)
.await;
return;
}
@ -472,7 +263,7 @@ impl ClientConnection {
let target_rho = match rho_manager::get_rho_con_for_user(receiver_id).await {
Some(rho) => rho,
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error)
self.send_error_response(cv.get_id(), CommunicationType::error)
.await;
return;
}
@ -483,11 +274,14 @@ impl ClientConnection {
// Create and send call distribution message
let forward = CommunicationValue::new(CommunicationType::call_invite)
.with_receiver(receiver_id)
.with_sender(sender_id)
.add_data_str(DataTypes::call_id, call_id.to_string())
.add_data_str(DataTypes::receiver_id, receiver_id.to_string())
.add_data_str(DataTypes::sender_id, sender_id.to_string());
.with_receiver(receiver_id as u64)
.with_sender(sender_id as u64)
.add_data(DataTypes::call_id, DataValue::Str(call_id.to_string()))
.add_data(
DataTypes::receiver_id,
DataValue::Str(receiver_id.to_string()),
)
.add_data(DataTypes::sender_id, DataValue::Str(sender_id.to_string()));
target_rho.message_to_client(forward).await;
@ -500,16 +294,16 @@ impl ClientConnection {
let user_id = self.get_user_id().await;
let call_id = match cv.get_data(DataTypes::call_id) {
Some(id_str) => match Uuid::parse_str(&id_str.to_string()) {
DataValue::Str(id_str) => match Uuid::parse_str(id_str.as_str()) {
Ok(id) => id,
Err(_) => {
self.send_error_response(&cv.get_id(), CommunicationType::error)
self.send_error_response(cv.get_id(), CommunicationType::error)
.await;
return;
}
},
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error)
self.send_error_response(cv.get_id(), CommunicationType::error)
.await;
return;
}
@ -518,33 +312,20 @@ impl ClientConnection {
if let Some(token) = call_manager::get_call_token(user_id, call_id).await {
let response = CommunicationValue::new(CommunicationType::call_token)
.with_id(cv.get_id())
.with_receiver(user_id)
.add_data_str(DataTypes::call_token, token);
.with_receiver(user_id as u64)
.add_data(DataTypes::call_token, DataValue::Str(token));
self.send_message(&response).await;
} else {
self.send_error_response(&cv.get_id(), CommunicationType::error)
self.send_error_response(cv.get_id(), CommunicationType::error)
.await;
return;
}
}
async fn handle_call_timeout_user(self: Arc<Self>, cv: CommunicationValue) {
let call_id = Uuid::from_str(
cv.get_data(DataTypes::call_id)
.unwrap_or(&JsonValue::Null)
.as_str()
.unwrap_or(""),
)
.unwrap();
let user_id = cv
.get_data(DataTypes::user_id)
.unwrap_or(&JsonValue::Null)
.as_i64()
.unwrap_or(0);
let untill = cv
.get_data(DataTypes::untill)
.unwrap_or(&JsonValue::Null)
.as_i64()
.unwrap_or(0);
let call_id =
Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap();
let user_id = cv.get_data(DataTypes::user_id).as_number().unwrap_or(0);
let untill = cv.get_data(DataTypes::untill).as_number().unwrap_or(0);
let call = call_manager::get_call(call_id).await;
if let Some(call) = call {
@ -554,8 +335,8 @@ impl ClientConnection {
.unwrap()
.has_admin()
{
let _ = call_util::remove_participant(call_id, user_id).await;
call.get_caller(user_id)
let _ = call_util::remove_participant(call_id, user_id as u64).await;
call.get_caller(user_id as u64)
.await
.unwrap()
.set_timeout(untill)
@ -564,18 +345,9 @@ impl ClientConnection {
}
}
async fn handle_call_disconnect_user(self: Arc<Self>, cv: CommunicationValue) {
let call_id = Uuid::from_str(
cv.get_data(DataTypes::call_id)
.unwrap_or(&JsonValue::Null)
.as_str()
.unwrap_or(""),
)
.unwrap();
let user_id = cv
.get_data(DataTypes::user_id)
.unwrap_or(&JsonValue::Null)
.as_i64()
.unwrap_or(0);
let call_id =
Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap();
let user_id = cv.get_data(DataTypes::user_id).as_number().unwrap_or(0);
let call = call_manager::get_call(call_id).await;
if let Some(call) = call {
@ -585,23 +357,14 @@ impl ClientConnection {
.unwrap()
.has_admin()
{
call.remove_caller(user_id).await;
call.remove_caller(user_id as u64).await;
}
}
}
async fn handle_call_set_anonymous_joining(self: Arc<Self>, cv: CommunicationValue) {
let call_id = Uuid::from_str(
cv.get_data(DataTypes::call_id)
.unwrap_or(&JsonValue::Null)
.as_str()
.unwrap_or(""),
)
.unwrap();
let enable = cv
.get_data(DataTypes::enabled)
.unwrap_or(&JsonValue::Null)
.as_bool()
.unwrap_or(true);
let call_id =
Uuid::from_str(cv.get_data(DataTypes::call_id).as_str().unwrap_or("")).unwrap();
let enable = cv.get_data(DataTypes::enabled).as_bool().unwrap_or(true);
let call = call_manager::get_call(call_id).await;
@ -620,10 +383,10 @@ impl ClientConnection {
let mut response_cv =
CommunicationValue::new(CommunicationType::call_set_anonymous_joining)
.with_id(cv.get_id())
.add_data(DataTypes::call_id, JsonValue::String(call_id.to_string()))
.add_data(DataTypes::enabled, JsonValue::Boolean(enable));
.add_data(DataTypes::call_id, DataValue::Str(call_id.to_string()))
.add_data(DataTypes::enabled, DataValue::Bool(enable));
if let Some(short_link) = short_link {
response_cv = response_cv.add_data(DataTypes::link, JsonValue::String(short_link));
response_cv = response_cv.add_data(DataTypes::link, DataValue::Str(short_link));
}
self.send_message(&response_cv).await;
}
@ -631,11 +394,13 @@ impl ClientConnection {
/// Forward message to Iota
async fn forward_to_iota(self: Arc<Self>, cv: CommunicationValue) {
if cv.is_type(CommunicationType::add_conversation)
&& cv.get_data(DataTypes::chat_partner_id).is_none()
&& cv
.get_data(DataTypes::chat_partner_id)
.as_number()
.is_some()
{
let chat_partner_name = cv
.get_data(DataTypes::chat_partner_name)
.unwrap_or(&JsonValue::Null)
.as_str()
.unwrap_or("")
.to_string();
@ -644,7 +409,7 @@ impl ClientConnection {
.await
.is_some()
{
self.send_error_response(&cv.get_id(), CommunicationType::error_anonymous)
self.send_error_response(cv.get_id(), CommunicationType::error_anonymous)
.await;
return;
}
@ -655,25 +420,22 @@ impl ClientConnection {
.with_id(cv.clone().get_id())
.add_data(
DataTypes::username,
JsonValue::from(chat_partner_name.clone()),
DataValue::Str(chat_partner_name.clone()),
),
Some(Duration::from_secs(20)),
)
.await;
let chat_partner_id = {
if let Ok(load_uuid_response) = load_uuid_response {
load_uuid_response
.get_data(DataTypes::user_id)
.unwrap_or(&JsonValue::Null)
.clone()
load_uuid_response.get_data(DataTypes::user_id).clone()
} else {
JsonValue::Null
DataValue::Null
}
};
if let Some(rho_conn) = self.get_rho_connection().await {
let updated_cv = cv
.with_sender(self.get_user_id().await)
.with_sender(self.get_user_id().await as u64)
.add_data(DataTypes::chat_partner_id, chat_partner_id);
rho_conn.message_to_iota(updated_cv).await;
}
@ -681,18 +443,14 @@ impl ClientConnection {
}
if let Some(rho_conn) = self.get_rho_connection().await {
let updated_cv = cv.with_sender(self.get_user_id().await);
let updated_cv = cv.with_sender(self.get_user_id().await as u64);
rho_conn.message_to_iota(updated_cv).await;
}
}
/// Send error response
async fn send_error_response(
self: Arc<Self>,
message_id: &Uuid,
error_type: CommunicationType,
) {
let error = CommunicationValue::new(error_type).with_id(*message_id);
async fn send_error_response(self: Arc<Self>, message_id: u32, error_type: CommunicationType) {
let error = CommunicationValue::new(error_type).with_id(message_id);
self.send_message(&error).await;
}
@ -704,8 +462,7 @@ impl ClientConnection {
}
*is_open_guard = false;
let mut session = self.sender.write().await;
let _ = session.close(None).await;
let _ = self.sender.close();
}
/// Set interested users list
@ -723,8 +480,8 @@ impl ClientConnection {
let interested_guard = self.clone().get_interested_users().await;
if interested_guard.contains(&user_id) {
let notification = CommunicationValue::new(CommunicationType::client_changed)
.add_data_str(DataTypes::user_id, user_id.to_string())
.add_data_str(DataTypes::user_state, format!("online"));
.add_data(DataTypes::user_id, DataValue::Str(user_id.to_string()))
.add_data(DataTypes::user_state, DataValue::Str("online".to_string()));
self.send_message(&notification).await;
}
@ -732,13 +489,11 @@ impl ClientConnection {
/// Handle connection close
pub async fn handle_close(&self) {
if self.is_identified().await {
let user_id = self.get_user_id().await;
if let Some(rho_conn) = rho_manager::get_rho_con_for_user(user_id).await {
rho_conn
.close_client_connection(Arc::new(self.clone()))
.await;
}
let user_id = self.get_user_id().await;
if let Some(rho_conn) = rho_manager::get_rho_con_for_user(user_id as i64).await {
rho_conn
.close_client_connection(Arc::new(self.clone()))
.await;
}
}
}
@ -749,10 +504,7 @@ impl Clone for ClientConnection {
Self {
sender: Arc::clone(&self.sender),
receiver: Arc::clone(&self.receiver),
user_id: Arc::clone(&self.user_id),
identified: Arc::clone(&self.identified),
challenged: Arc::clone(&self.challenged),
challenge: Arc::clone(&self.challenge),
user_id: self.user_id,
ping: Arc::clone(&self.ping),
pub_key: Arc::clone(&self.pub_key),
rho_connection: Arc::clone(&self.rho_connection),

174
src/rho/connection.rs Normal file
View file

@ -0,0 +1,174 @@
use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue};
use epsilon_native::{Receiver, Sender};
use rand::{Rng, distributions::Alphanumeric};
use std::{sync::Arc, time::Duration};
use tokio::sync::RwLock;
use crate::{
anonymous_clients::anonymous_client_connection::AnonymousClientConnection,
get_private_key, get_public_key,
omega::omega_connection::get_omega_connection,
rho::{client_connection::ClientConnection, iota_connection::IotaConnection},
util::{
crypto_helper::{load_public_key, public_key_to_base64},
crypto_util::{DataFormat, SecurePayload},
},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionKind {
Client,
Iota,
AnonymousClient,
Phi,
}
pub struct GeneralConnection {
pub sender: Arc<Sender>,
pub receiver: Arc<Receiver>,
identified: Arc<RwLock<bool>>,
challenged: Arc<RwLock<bool>>,
challenge: Arc<RwLock<String>>,
connection_kind: Arc<RwLock<Option<ConnectionKind>>>,
id: Arc<RwLock<u64>>,
pub_key: Arc<RwLock<Option<Vec<u8>>>>,
}
impl GeneralConnection {
pub fn new(sender: Sender, receiver: Receiver) -> Arc<Self> {
Arc::new(Self {
sender: Arc::new(sender),
receiver: Arc::new(receiver),
identified: Arc::new(RwLock::new(false)),
challenged: Arc::new(RwLock::new(false)),
challenge: Arc::new(RwLock::new(String::new())),
connection_kind: Arc::new(RwLock::new(None)),
id: Arc::new(RwLock::new(0)),
pub_key: Arc::new(RwLock::new(None)),
})
}
}
impl GeneralConnection {
pub async fn handle(self: Arc<Self>) {
loop {
let cv = match self.receiver.receive().await {
Ok(v) => v,
Err(_) => break,
};
if !*self.identified.read().await {
self.handle_identification(cv).await;
continue;
}
if !*self.challenged.read().await {
self.handle_challenge_response(cv).await;
continue;
}
if self.migrate().await {
break;
}
}
}
async fn handle_identification(self: &Arc<Self>, cv: CommunicationValue) {
if !cv.is_type(CommunicationType::identification) {
return;
}
if let DataValue::Number(iota_id) = cv.get_data(DataTypes::iota_id) {
*self.id.write().await = *iota_id as u64;
*self.connection_kind.write().await = Some(ConnectionKind::Iota);
let get_pub_key_msg = CommunicationValue::new(CommunicationType::get_iota_data)
.add_data(DataTypes::iota_id, DataValue::Number(*iota_id));
let response_cv = get_omega_connection()
.await_response(&get_pub_key_msg, Some(Duration::from_secs(20)))
.await;
let response_cv = match response_cv {
Ok(r) => r,
Err(_) => return,
};
let base64_pub = response_cv
.get_data(DataTypes::public_key)
.as_str()
.unwrap_or("");
let pub_key = match load_public_key(base64_pub) {
Some(pk) => pk,
None => return,
};
*self.pub_key.write().await = Some(pub_key.as_bytes().to_vec());
let challenge: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect();
*self.challenge.write().await = challenge.clone();
*self.identified.write().await = true;
let encrypted_challenge =
SecurePayload::new(&challenge, DataFormat::Base64, get_private_key())
.unwrap()
.encrypt_x448(pub_key)
.unwrap()
.export(DataFormat::Base64);
let response = CommunicationValue::new(CommunicationType::challenge)
.add_data(
DataTypes::public_key,
DataValue::Str(public_key_to_base64(&get_public_key())),
)
.add_data(DataTypes::challenge, DataValue::Str(encrypted_challenge));
let _ = self.sender.send(&response).await;
}
}
async fn handle_challenge_response(self: &Arc<Self>, cv: CommunicationValue) {
if !cv.is_type(CommunicationType::challenge_response) {
return;
}
if let DataValue::Str(response) = cv.get_data(DataTypes::challenge) {
if *response == *self.challenge.read().await {
*self.challenged.write().await = true;
}
}
}
async fn migrate(self: &Arc<Self>) -> bool {
let kind = match *self.connection_kind.read().await {
Some(kind) => kind,
None => return false,
};
let id = *self.id.read().await;
match kind {
ConnectionKind::Client => {
let client = ClientConnection::from_general(self.clone(), id).await;
client.start();
}
ConnectionKind::Iota => {
let iota = IotaConnection::from_general(self.clone(), id).await;
iota.start();
}
ConnectionKind::AnonymousClient => {
let client = AnonymousClientConnection::from_general(self.clone(), id).await;
client.start();
}
ConnectionKind::Phi => {
let iota = ClientConnection::from_general(self.clone(), id).await;
iota.start();
}
}
true
}
}

View file

@ -1,24 +1,18 @@
use crate::calls::call_group::CallGroup;
use crate::calls::call_manager;
use crate::get_private_key;
use crate::get_public_key;
use crate::log_cv_in;
use crate::log_cv_out;
use crate::log_err;
use crate::log_in;
use crate::log_out;
use crate::omega::omega_connection::get_omega_connection;
use crate::util::crypto_helper::load_public_key;
use crate::util::crypto_helper::public_key_to_base64;
use crate::util::crypto_util::DataFormat;
use crate::util::crypto_util::SecurePayload;
use crate::rho::connection::GeneralConnection;
use crate::util::logger::PrintType;
use async_tungstenite::WebSocketReceiver;
use async_tungstenite::WebSocketSender;
use async_tungstenite::tungstenite::Message;
use dashmap::DashMap;
use json::JsonValue;
use rand::Rng;
use rand::distributions::Alphanumeric;
use epsilon_core::CommunicationType;
use epsilon_core::CommunicationValue;
use epsilon_core::DataTypes;
use epsilon_core::DataValue;
use epsilon_native::Receiver;
use epsilon_native::Sender;
use std::{
collections::HashMap,
sync::{Arc, Weak},
@ -26,56 +20,48 @@ use std::{
};
use tokio::sync::RwLock;
use tokio::sync::mpsc;
use tokio_util::compat::Compat;
use tungstenite::Utf8Bytes;
use uuid::Uuid;
use x448::PublicKey;
use super::{rho_connection::RhoConnection, rho_manager};
use crate::{
data::communication::{CommunicationType, CommunicationValue, DataTypes},
omega::omega_connection::OmegaConnection,
};
use crate::omega::omega_connection::OmegaConnection;
pub struct IotaConnection {
pub sender: Arc<RwLock<WebSocketSender<Compat<tokio::net::TcpStream>>>>,
pub receiver: Arc<RwLock<WebSocketReceiver<Compat<tokio::net::TcpStream>>>>,
pub iota_id: Arc<RwLock<i64>>,
pub user_ids: Arc<RwLock<Vec<i64>>>,
identified: Arc<RwLock<bool>>,
challenged: Arc<RwLock<bool>>,
challenge: Arc<RwLock<String>>,
pub iota_id: u64,
pub sender: Arc<Sender>,
pub receiver: Arc<Receiver>,
pub user_ids: Arc<RwLock<Vec<u64>>>,
pub ping: Arc<RwLock<i64>>,
pub_key: Arc<RwLock<Option<Vec<u8>>>>,
pub waiting_tasks:
DashMap<Uuid, Box<dyn Fn(Arc<IotaConnection>, CommunicationValue) -> bool + Send + Sync>>,
DashMap<u32, Box<dyn Fn(Arc<IotaConnection>, CommunicationValue) -> bool + Send + Sync>>,
pub rho_connection: Arc<RwLock<Option<Weak<RhoConnection>>>>,
}
impl IotaConnection {
/// Create a new IotaConnection
pub fn new(
sender: WebSocketSender<Compat<tokio::net::TcpStream>>,
receiver: WebSocketReceiver<Compat<tokio::net::TcpStream>>,
) -> Arc<Self> {
pub async fn from_general(general: Arc<GeneralConnection>, iota_id: u64) -> Arc<Self> {
Arc::new(Self {
sender: Arc::new(RwLock::new(sender)),
receiver: Arc::new(RwLock::new(receiver)),
iota_id: Arc::new(RwLock::new(0)),
user_ids: Arc::new(RwLock::new(Vec::new())),
identified: Arc::new(RwLock::new(false)),
challenged: Arc::new(RwLock::new(false)),
challenge: Arc::new(RwLock::new(String::new())),
ping: Arc::new(RwLock::new(0)),
pub_key: Arc::new(RwLock::new(None)),
waiting_tasks: DashMap::new(),
rho_connection: Arc::new(RwLock::new(None)),
user_ids: Arc::new(RwLock::new(Vec::new())),
sender: general.sender.clone(),
receiver: general.receiver.clone(),
iota_id: iota_id,
waiting_tasks: DashMap::new(),
})
}
pub fn start(self: Arc<Self>) {
let self_clone = self.clone();
tokio::spawn(async move {
while let Ok(cv) = self_clone.receiver.receive().await {
self_clone.clone().handle_message(cv).await;
}
});
}
/// Get the Iota ID
pub async fn get_iota_id(&self) -> i64 {
*self.iota_id.read().await
pub async fn get_iota_id(&self) -> u64 {
self.iota_id
}
pub async fn get_public_key(&self) -> Option<PublicKey> {
@ -87,15 +73,10 @@ impl IotaConnection {
}
/// Get the user IDs
pub async fn get_user_ids(&self) -> Vec<i64> {
pub async fn get_user_ids(&self) -> Vec<u64> {
self.user_ids.read().await.clone()
}
/// Check if connection is identified
pub async fn is_identified(&self) -> bool {
*self.identified.read().await
}
/// Get current ping
pub async fn get_ping(&self) -> i64 {
*self.ping.read().await
@ -117,38 +98,16 @@ impl IotaConnection {
}
}
/// Send a message to the Iota
pub async fn send_message_str(&self, message: &str) {
let mut session = self.sender.write().await;
if let Err(e) = session
.send(Message::Text(Utf8Bytes::from(message.to_string())))
.await
{
log_err!(
self.get_iota_id().await,
PrintType::Iota,
"Failed to send WebSocket message: {:?}",
e,
);
}
}
/// Send a CommunicationValue to the Iota
pub async fn send_message(&self, cv: &CommunicationValue) {
if !cv.is_type(CommunicationType::pong) {
log_out!(
self.get_iota_id().await,
PrintType::Iota,
"{}",
cv.to_json().to_string()
);
log_cv_out!(PrintType::Iota, cv);
}
self.send_message_str(&cv.to_json().to_string()).await;
self.sender.send(&cv).await;
}
/// Handle incoming message from Iota
pub async fn handle_message(self: Arc<Self>, message: Utf8Bytes) {
let cv = CommunicationValue::from_json(&message);
pub async fn handle_message(self: Arc<Self>, cv: CommunicationValue) {
// Handle ping
if cv.is_type(CommunicationType::ping) || cv.is_type(CommunicationType::pong) {
self.handle_ping(cv).await;
@ -157,261 +116,6 @@ impl IotaConnection {
log_cv_in!(PrintType::Iota, cv);
let identified = *self.identified.read().await;
let challenged = *self.challenged.read().await;
if !identified && cv.is_type(CommunicationType::identification) {
let iota_id = cv
.get_data(DataTypes::iota_id)
.and_then(|v| v.as_i64())
.unwrap_or(0);
if iota_id == 0 {
log_out!(self.get_iota_id().await, PrintType::Iota, "Invalid IOTA ID");
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_data)
.await;
self.close().await;
return;
}
*self.iota_id.write().await = iota_id;
let get_pub_key_msg = CommunicationValue::new(CommunicationType::get_iota_data)
.with_id(cv.get_id())
.add_data(DataTypes::iota_id, JsonValue::from(iota_id));
let response_cv = get_omega_connection()
.await_response(&get_pub_key_msg, Some(Duration::from_secs(20)))
.await;
if let Ok(response_cv) = response_cv {
if !response_cv.is_type(CommunicationType::get_iota_data) {
self.send_error_response(&cv.get_id(), CommunicationType::error_internal)
.await;
self.close().await;
return;
}
let base64_pub = response_cv
.get_data(DataTypes::public_key)
.and_then(|v| v.as_str())
.unwrap_or("");
let pub_key = match load_public_key(base64_pub) {
Some(pk) => pk,
_ => {
self.send_error_response(
&cv.get_id(),
CommunicationType::error_invalid_public_key,
)
.await;
self.close().await;
return;
}
};
*self.pub_key.write().await = Some(pub_key.as_bytes().to_vec());
let challenge: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect();
*self.challenge.write().await = challenge.clone();
let encrypted_challenge =
SecurePayload::new(&challenge, DataFormat::Base64, get_private_key())
.unwrap()
.encrypt_x448(pub_key)
.unwrap()
.export(DataFormat::Base64);
*self.identified.write().await = true;
let challenge_msg = CommunicationValue::new(CommunicationType::challenge)
.with_id(cv.get_id())
.add_data_str(
DataTypes::public_key,
public_key_to_base64(&get_public_key()),
)
.add_data_str(DataTypes::challenge, encrypted_challenge);
self.send_message(&challenge_msg).await;
} else {
self.send_error_response(&cv.get_id(), CommunicationType::error_internal)
.await;
self.close().await;
return;
}
return;
} else if !identified && cv.is_type(CommunicationType::register_iota) {
let base64_pub = cv
.get_data(DataTypes::public_key)
.and_then(|v| v.as_str())
.unwrap_or("");
if base64_pub.is_empty() {
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_public_key)
.await;
self.close().await;
return;
}
let register_msg = CommunicationValue::new(CommunicationType::complete_register_iota)
.with_id(cv.get_id())
.add_data(
DataTypes::public_key,
JsonValue::String(base64_pub.to_string()),
);
let iota_conn_clone = self.clone();
let register_response: Result<CommunicationValue, _> = get_omega_connection()
.await_response(&register_msg, Some(Duration::from_secs(20)))
.await;
let iota_conn_for_task = iota_conn_clone.clone();
if let Ok(register_response) = register_response {
if !register_response.is_type(CommunicationType::complete_register_iota) {
iota_conn_for_task
.send_error_response(&cv.get_id(), CommunicationType::error_internal)
.await;
iota_conn_for_task.close().await;
return;
}
let new_iota_id = register_response
.get_data(DataTypes::iota_id)
.and_then(|v| v.as_i64())
.unwrap_or(0);
if new_iota_id == 0 {
iota_conn_for_task
.send_error_response(&cv.get_id(), CommunicationType::error_internal)
.await;
iota_conn_for_task.close().await;
return;
}
*iota_conn_for_task.iota_id.write().await = new_iota_id;
*iota_conn_for_task.identified.write().await = true;
let success_msg = CommunicationValue::new(CommunicationType::success)
.with_id(cv.get_id())
.add_data(DataTypes::iota_id, JsonValue::from(new_iota_id));
iota_conn_for_task.send_message(&success_msg).await;
} else {
iota_conn_for_task
.send_error_response(&cv.get_id(), CommunicationType::error_internal)
.await;
iota_conn_for_task.close().await;
}
return;
}
if identified && !challenged && cv.is_type(CommunicationType::challenge_response) {
let client_response = cv
.get_data(DataTypes::challenge)
.and_then(|v| v.as_str())
.unwrap_or("");
if client_response == *self.challenge.read().await {
*self.challenged.write().await = true;
let iota_id = self.get_iota_id().await;
if rho_manager::contains_iota(iota_id).await {
if let Some(existing_rho) = rho_manager::get_rho_by_iota(iota_id).await {
existing_rho.close_iota_connection().await;
}
}
// Inform Omega & Verify Users
let iota_users_cv = get_omega_connection()
.await_response(
&CommunicationValue::new(CommunicationType::iota_connected).add_data(
DataTypes::iota_id,
JsonValue::from(self.get_iota_id().await),
),
Some(Duration::from_secs(20)),
)
.await;
let mut user_ids: Vec<i64> = Vec::new();
if let Ok(iota_users_cv) = iota_users_cv {
if !iota_users_cv.is_type(CommunicationType::iota_user_data) {
log_err!(
self.get_iota_id().await,
PrintType::Omikron,
"Invalid communication type {:?}",
iota_users_cv.get_type()
);
return;
}
let val_user_ids = iota_users_cv.get_data(DataTypes::user_ids).unwrap().clone();
match val_user_ids {
JsonValue::Array(arr) => {
for item in arr {
if let JsonValue::Number(_) = item {
user_ids.push(item.as_i64().unwrap_or(0));
}
}
}
_ => {}
}
} else {
log_err!(
self.get_iota_id().await,
PrintType::Omikron,
"Failed to retrieve user IDs"
);
}
log_in!(
self.get_iota_id().await,
PrintType::General,
"User IDs: {:?}",
user_ids.clone()
);
*self.user_ids.write().await = user_ids.clone();
let rho_connection =
Arc::new(RhoConnection::new(self.clone(), user_ids.clone()).await);
self.set_rho_connection(Arc::downgrade(&rho_connection))
.await;
rho_manager::add_rho(rho_connection).await;
let mut str = String::new();
for id in &user_ids {
str.push_str(&format!(",{}", id));
}
if !str.is_empty() {
str.remove(0);
}
self.send_message(
&CommunicationValue::new(CommunicationType::identification_response)
.with_id(cv.get_id())
.add_data_str(DataTypes::accepted_ids, str)
.add_data_str(DataTypes::accepted, user_ids.len().to_string()),
)
.await;
} else {
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_challenge)
.await;
self.close().await;
}
return;
}
if !self.is_identified().await {
self.send_error_response(&cv.get_id(), CommunicationType::error_not_authenticated)
.await;
self.close().await;
return;
}
// Handle GET_CHATS
if cv.is_type(CommunicationType::get_chats) {
self.handle_get_chats(cv).await;
@ -420,7 +124,7 @@ impl IotaConnection {
// Handle forwarding to other Iotas or clients
let receiver_id = cv.get_receiver();
if (receiver_id != 0 && !self.get_user_ids().await.contains(&receiver_id))
if (receiver_id != 0 && !self.get_user_ids().await.contains(&(receiver_id as u64)))
|| cv.is_type(CommunicationType::message_other_iota)
|| cv.is_type(CommunicationType::send_chat)
{
@ -437,31 +141,28 @@ impl IotaConnection {
|| cv.is_type(CommunicationType::delete_iota)
{
let sender = self.get_iota_id().await;
self.handle_omega_forward(cv.with_sender(sender)).await;
self.handle_omega_forward(cv.with_sender(sender as u64))
.await;
return;
}
// Forward to client
self.forward_to_client(cv).await;
}
async fn send_error_response(&self, message_id: &Uuid, error_type: CommunicationType) {
let error = CommunicationValue::new(error_type).with_id(*message_id);
async fn send_error_response(&self, message_id: u32, error_type: CommunicationType) {
let error = CommunicationValue::new(error_type).with_id(message_id);
self.send_message(&error).await;
}
async fn close(&self) {
let mut sender = self.sender.write().await;
let _ = sender.close(None).await;
let _ = self.sender.close();
}
async fn handle_omega_forward(self: Arc<Self>, cv: CommunicationValue) {
let iota_for_closure = self.clone();
tokio::spawn(async move {
let response_cv = get_omega_connection()
.await_response(
&cv.with_sender(*self.iota_id.read().await),
Some(Duration::from_secs(20)),
)
.await_response(&cv.with_sender(self.iota_id), Some(Duration::from_secs(20)))
.await;
if let Ok(response_cv) = response_cv {
iota_for_closure.send_message(&response_cv).await;
@ -470,7 +171,7 @@ impl IotaConnection {
}
/// Handle ping message
async fn handle_ping(&self, cv: CommunicationValue) {
if let Some(last_ping) = cv.get_data(DataTypes::last_ping) {
if let DataValue::Number(last_ping) = cv.get_data(DataTypes::last_ping) {
if let Ok(ping_val) = last_ping.to_string().parse::<i64>() {
let mut ping_guard = self.ping.write().await;
*ping_guard = ping_val;
@ -483,13 +184,13 @@ impl IotaConnection {
HashMap::new()
};
let pings = client_pings
let pings: Vec<(DataTypes, DataValue)> = client_pings
.into_iter()
.map(|(k, v)| (k, JsonValue::String(v.to_string())))
.map(|(k, v)| (DataTypes::parse(k), DataValue::Number(v)))
.collect();
let response = CommunicationValue::new(CommunicationType::pong)
.with_id(cv.get_id())
.add_data(DataTypes::ping_clients, JsonValue::Object(pings));
.add_data(DataTypes::ping_clients, DataValue::Container(pings));
self.send_message(&response).await;
}
@ -498,8 +199,8 @@ impl IotaConnection {
let receiver_id = cv.get_receiver();
let sender_id = cv.get_sender();
if self.get_user_ids().await.contains(&sender_id) {
if let Some(target_rho) = rho_manager::get_rho_con_for_user(receiver_id).await {
if self.get_user_ids().await.contains(&(sender_id as u64)) {
if let Some(target_rho) = rho_manager::get_rho_con_for_user(receiver_id as i64).await {
target_rho.message_to_iota(cv).await;
} else {
let error = CommunicationValue::new(CommunicationType::error_no_iota)
@ -511,7 +212,7 @@ impl IotaConnection {
self.send_message(
&CommunicationValue::new(CommunicationType::error_invalid_user_id).add_data(
DataTypes::error_type,
JsonValue::String(
DataValue::Str(
"You are sending to another User without authority.".to_string(),
),
),
@ -525,77 +226,98 @@ impl IotaConnection {
let receiver_id = cv.get_receiver();
let mut interested_ids: Vec<i64> = Vec::new();
// loading Calls
// ============================
// Load Calls
// ============================
let calls: Vec<Arc<CallGroup>> = call_manager::get_call_groups(receiver_id).await;
let mut invites: HashMap<i64, Vec<JsonValue>> = HashMap::new();
let empty = &calls.is_empty();
let mut invites: HashMap<i64, Vec<DataValue>> = HashMap::new();
let empty = calls.is_empty();
for call in calls {
for inviter in call.members.read().await.iter() {
let call_self = call.get_caller(receiver_id).await.unwrap();
let admin = call_self.has_admin();
let inviter_id = inviter.user_id;
let timeout = *call_self.timeout.read().await;
let admin = call_self.has_admin();
// Build call container
let mut call_map: HashMap<DataTypes, DataValue> = HashMap::new();
call_map.insert(DataTypes::call_id, DataValue::Str(call.call_id.to_string()));
let mut call_obj = JsonValue::new_object();
let _ = call_obj.insert("call_id", JsonValue::String(call.call_id.to_string()));
if timeout > 0 {
let _ = call_obj.insert("timeout", JsonValue::from(timeout));
}
if admin {
let _ = call_obj.insert("admin", JsonValue::Boolean(admin));
call_map.insert(DataTypes::timeout, DataValue::Number(timeout as i64));
}
if let Some(call_ids) = invites.get_mut(&inviter_id) {
call_ids.push(call_obj);
} else {
invites.insert(inviter_id, vec![call_obj]);
if admin {
call_map.insert(DataTypes::has_admin, DataValue::Bool(true));
}
let call_container = DataValue::container_from_map(&call_map);
invites
.entry(inviter_id as i64)
.or_insert_with(Vec::new)
.push(call_container);
}
}
// Process contacts and add call information
let enriched_contacts = if *empty {
if let Some(contacts_data) = cv.get_data(DataTypes::user_ids) {
log_in!(self.get_iota_id().await, PrintType::Call, "Call empty");
contacts_data.clone()
} else {
log_in!(
self.get_iota_id().await,
PrintType::Call,
"Call empty No Data"
);
JsonValue::new_array()
// ============================
// Enrich Contacts
// ============================
let enriched_contacts = if empty {
match cv.get_data(DataTypes::user_ids) {
DataValue::Array(arr) => DataValue::Array(arr.clone()),
_ => DataValue::Array(vec![]),
}
} else {
let mut enrc_contacts = JsonValue::new_array();
if let Some(contacts_data) = cv.get_data(DataTypes::user_ids) {
if let JsonValue::Array(user_ids) = contacts_data {
for user_json in user_ids {
let user_id = user_json["user_id"].as_i64().unwrap_or(0);
interested_ids.push(user_id);
let mut enriched_contact = user_json.clone();
if let Some(calls) = invites.get(&user_id) {
let _ =
enriched_contact.insert("calls", JsonValue::Array(calls.clone()));
let mut enriched: Vec<DataValue> = Vec::new();
if let DataValue::Array(users) = cv.get_data(DataTypes::user_ids) {
for user_val in users {
if let DataValue::Container(entries) = user_val {
let mut user_map: HashMap<DataTypes, DataValue> =
entries.iter().cloned().collect();
// extract user_id
if let Some(DataValue::Number(user_id)) = user_map.get(&DataTypes::user_id)
{
interested_ids.push(*user_id);
// attach calls if exists
if let Some(call_list) = invites.get(user_id) {
user_map
.insert(DataTypes::calls, DataValue::Array(call_list.clone()));
}
}
let _ = enrc_contacts.push(enriched_contact);
enriched.push(DataValue::container_from_map(&user_map));
}
} else {
enrc_contacts = contacts_data.clone();
}
}
enrc_contacts
DataValue::Array(enriched)
};
// Notify OmegaConnection about user states
OmegaConnection::user_states(receiver_id, interested_ids.clone()).await;
// ============================
// Notify Omega
// ============================
OmegaConnection::user_states(receiver_id as i64, interested_ids.clone()).await;
// Set interested users in RhoConnection
// ============================
// Notify Rho
// ============================
if let Some(rho_conn) = self.get_rho_connection().await {
rho_conn.set_interested(receiver_id, interested_ids).await;
rho_conn
.set_interested(receiver_id as i64, interested_ids)
.await;
}
// ============================
// Forward to client
// ============================
self.forward_to_client(cv.add_data(DataTypes::user_ids, enriched_contacts))
.await;
}
@ -607,7 +329,7 @@ impl IotaConnection {
rho_conn.message_to_client(updated_cv).await;
} else {
log_err!(
self.get_iota_id().await,
self.get_iota_id().await as i64,
PrintType::General,
"Failed to forward message to client"
);
@ -615,10 +337,8 @@ impl IotaConnection {
}
pub async fn handle_close(&self) {
if self.is_identified().await {
if let Some(rho_conn) = self.get_rho_connection().await {
rho_conn.close_iota_connection().await;
}
if let Some(rho_conn) = self.get_rho_connection().await {
rho_conn.close_iota_connection().await;
}
}
@ -638,7 +358,7 @@ impl IotaConnection {
tokio::spawn(async move {
if let Err(e) = inner_tx.send(response_cv).await {
log_err!(
io.get_iota_id().await,
io.get_iota_id().await as i64,
PrintType::Iota,
"Failed to send response back to awaiter: {}",
e

View file

@ -1,4 +1,6 @@
pub mod client_connection;
pub mod connection;
pub mod iota_connection;
pub mod rho_connection;
pub mod rho_manager;
pub mod server;

View file

@ -1,10 +1,8 @@
use super::{client_connection::ClientConnection, iota_connection::IotaConnection, rho_manager};
use crate::data::{
communication::{CommunicationType, CommunicationValue, DataTypes},
user::UserStatus,
};
use crate::data::user::UserStatus;
use crate::omega::omega_connection::OmegaConnection;
use json::{JsonValue, number::Number};
use epsilon_core::{CommunicationType, CommunicationValue, DataTypes, DataValue};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
@ -27,8 +25,8 @@ impl RhoConnection {
rho_connection
}
pub async fn get_iota_id(&self) -> i64 {
self.iota_connection.get_iota_id().await
pub async fn get_iota_id(&self) -> u64 {
self.iota_connection.iota_id
}
pub fn get_user_ids(&self) -> &Vec<i64> {
@ -52,7 +50,7 @@ impl RhoConnection {
let connections = self.client_connections.read().await;
let mut collections = Vec::new();
for con in connections.iter() {
if con.get_user_id().await == user_id {
if con.get_user_id().await == user_id as u64 {
collections.push(con.clone());
}
}
@ -63,7 +61,7 @@ impl RhoConnection {
pub async fn add_client_connection(&self, connection: Arc<ClientConnection>) {
let notification = CommunicationValue::new(CommunicationType::client_connected).add_data(
DataTypes::user_id,
JsonValue::Number(Number::from(connection.get_user_id().await)),
DataValue::Number(connection.get_user_id().await as i64),
);
self.iota_connection.send_message(&notification).await;
@ -74,8 +72,8 @@ impl RhoConnection {
}
OmegaConnection::client_changed(
self.get_iota_id().await,
connection.get_user_id().await,
self.get_iota_id().await as i64,
connection.get_user_id().await as i64,
UserStatus::user_online,
)
.await;
@ -93,8 +91,8 @@ impl RhoConnection {
// Notify OmegaConnection
OmegaConnection::client_changed(
self.get_iota_id().await,
connection.get_user_id().await,
self.get_iota_id().await as i64,
connection.get_user_id().await as i64,
UserStatus::user_offline,
)
.await;
@ -109,10 +107,10 @@ impl RhoConnection {
}
// Remove from manager
rho_manager::remove_rho(self.get_iota_id().await).await;
rho_manager::remove_rho(self.get_iota_id().await as i64).await;
// Notify OmegaConnection
OmegaConnection::close_iota(self.get_iota_id().await).await;
OmegaConnection::close_iota(self.get_iota_id().await as i64).await;
}
/// Send message from Iota to specific client
@ -136,7 +134,7 @@ impl RhoConnection {
let connections = self.client_connections.read().await;
for connection in connections.iter() {
let conn_user_id = connection.get_user_id().await;
if conn_user_id == user_id {
if conn_user_id == user_id as u64 {
connection
.clone()
.set_interested_users(interested_ids.clone())

View file

@ -41,7 +41,7 @@ pub async fn remove_rho(iota_id: i64) -> Option<Arc<RhoConnection>> {
pub async fn add_rho(rho_connection: Arc<RhoConnection>) {
let mut connections = RHO_CONNECTIONS.write().await;
let iota_id = rho_connection.get_iota_id().await;
connections.insert(iota_id, rho_connection);
connections.insert(iota_id as i64, rho_connection);
}
/// Get a RhoConnection by Iota ID directly

57
src/rho/server.rs Normal file
View file

@ -0,0 +1,57 @@
use crate::{rho::connection::GeneralConnection, util::file_util::load_file_buf};
use epsilon_native::Host;
use quinn::ServerConfig;
use rustls::pki_types::PrivateKeyDer;
use std::sync::Arc;
use tokio::io::unix::AsyncFd;
pub async fn start(port: u16) {
let tls_cfg = load_tls().expect("TLS config failed");
let server_crypto = quinn::crypto::rustls::QuicServerConfig::try_from(tls_cfg)
.expect("Failed to convert to QuicServerConfig");
let server_cfg = ServerConfig::with_crypto(Arc::new(server_crypto));
let mut host: Host = epsilon_native::host(port, server_cfg).await.unwrap();
tokio::spawn(async move {
while let Some((sender, receiver)) = host.next().await {
tokio::spawn(async move {
GeneralConnection::new(sender, receiver).handle().await;
});
}
});
}
fn load_tls() -> Option<rustls::ServerConfig> {
let mut cert_file_buf = load_file_buf("certs", "cert.pem").ok()?;
let mut key_file_buf = load_file_buf("certs", "cert.key").ok()?;
let cert_chain = rustls_pemfile::certs(&mut cert_file_buf)
.collect::<Result<Vec<_>, _>>()
.ok()?;
let mut keys: Vec<PrivateKeyDer> = rustls_pemfile::pkcs8_private_keys(&mut key_file_buf)
.map(|k| k.map(Into::into))
.collect::<Result<Vec<_>, _>>()
.ok()?;
if keys.is_empty() {
let mut key_file_buf = load_file_buf("certs", "cert.key").ok()?;
keys = rustls_pemfile::rsa_private_keys(&mut key_file_buf)
.map(|k| k.map(Into::into))
.collect::<Result<Vec<_>, _>>()
.ok()?;
}
if keys.is_empty() {
return None;
}
let cfg = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, keys.remove(0))
.ok()?;
Some(cfg)
}

179
src/util/file_util.rs Normal file
View file

@ -0,0 +1,179 @@
use std::fs::{self, File};
use std::io::{self, BufReader, Read};
use std::path::{Path, PathBuf};
use crate::log;
use crate::util::logger::PrintType;
#[allow(dead_code)]
pub fn delete_directory(path: &str) -> bool {
let dir = Path::new(&get_directory()).join(path);
delete_dir_recursive(&dir)
}
#[allow(dead_code)]
fn delete_dir_recursive(directory: &Path) -> bool {
if !directory.exists() {
return false;
}
if let Err(e) = fs::remove_dir_all(directory) {
log!(
0,
PrintType::General,
"[IMPORTANT] Couldn't delete directory {}: {}",
directory.display(),
e,
);
return false;
}
true
}
#[allow(dead_code)]
pub fn delete_user_directory(user_id: i64) {
let user_dir = Path::new(&get_directory())
.join("users")
.join(user_id.to_string());
let _ = delete_dir_recursive(&user_dir);
}
pub fn load_file_buf(path: &str, name: &str) -> io::Result<BufReader<File>> {
let dir = Path::new(&get_directory()).join(path);
let file_path = dir.join(name);
// Ensure the directory exists, create if necessary
if !dir.exists() {
if let Err(_) = fs::create_dir_all(&dir) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"Directory creation failed",
));
}
}
// Create the file if it doesn't exist
if !file_path.exists() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"File creation failed",
));
}
// Open the file and return a BufReader for efficient reading
let file = File::open(&file_path)?;
Ok(BufReader::new(file))
}
pub fn has_file(path: &str, name: &str) -> bool {
let dir = Path::new(&get_directory()).join(path);
let file_path = dir.join(name);
if !dir.exists() {
return false;
}
if !file_path.exists() {
return false;
}
true
}
pub fn has_dir(path: &str) -> bool {
let dir = Path::new(&get_directory()).join(path);
if !dir.exists() {
return false;
}
true
}
pub fn load_file(path: &str, name: &str) -> String {
let dir = Path::new(&get_directory()).join(path);
let file_path = dir.join(name);
if !dir.exists() {
if let Err(e) = fs::create_dir_all(&dir) {
log!(
0,
PrintType::General,
"[IMPORTANT] Couldn't create directories: {}",
e
);
return String::new();
}
return String::new();
}
if !file_path.exists() {
if let Err(e) = File::create(&file_path) {
log!(
0,
PrintType::General,
"[IMPORTANT] Couldn't create file: {}",
e
);
}
return String::new();
}
let mut content = String::new();
if let Ok(mut f) = File::open(&file_path) {
let _ = f.read_to_string(&mut content);
}
content
}
pub fn load_file_vec(path: &str, name: &str) -> Result<Vec<u8>, std::io::Error> {
let dir = Path::new(&get_directory()).join(path);
let file_path = dir.join(name);
std::fs::read(file_path)
}
pub fn save_file(path: &str, name: &str, value: &str) {
let dir = Path::new(&get_directory()).join(path);
let file_path = dir.join(name);
if !dir.exists() {
if let Err(e) = fs::create_dir_all(&dir) {
log!(
0,
PrintType::General,
"[IMPORTANT] Couldn't create directories: {}",
e
);
return;
}
}
if let Err(e) = fs::write(&file_path, value) {
log!(
0,
PrintType::General,
"[IMPORTANT] Couldn't write file {}: {}",
file_path.display(),
e
);
}
}
pub fn get_children(path: &str) -> Vec<String> {
let dir = Path::new(&get_directory()).join(path);
let mut children = Vec::new();
if let Ok(entries) = fs::read_dir(&dir) {
for entry in entries {
if let Ok(entry) = entry {
children.push(entry.file_name().to_string_lossy().to_string());
}
}
}
children
}
pub fn get_directory() -> String {
let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("."));
exe.parent()
.unwrap_or(Path::new("."))
.to_string_lossy()
.to_string()
}

View file

@ -1,4 +1,5 @@
use std::{
collections::HashMap,
fs::{self, OpenOptions},
io::Write,
path::Path,
@ -8,9 +9,7 @@ use std::{
};
use ansi_term::Color;
use json::JsonValue;
use crate::data::communication::CommunicationValue;
use epsilon_core::{CommunicationValue, DataTypes, DataValue};
static LOGGER: OnceLock<mpsc::Sender<LogMessage>> = OnceLock::new();
@ -151,7 +150,7 @@ pub fn log_cv_internal(
let formatted = format_cv(cv);
log_internal(
cv.get_sender(),
cv.get_sender() as i64,
print_type.unwrap_or(PrintType::General),
prefix,
false,
@ -176,24 +175,80 @@ pub fn format_cv(cv: &CommunicationValue) -> String {
let comm_type = cv.get_type().to_string();
parts.push(format!("{}", comm_type));
let mut data_parts = Vec::new();
if let JsonValue::Object(data) = &cv.clone().to_json()["data"] {
for (key, value) in data.iter() {
let val_string = match value {
JsonValue::String(s) => s.clone(),
_ => value.dump(),
};
let data: &HashMap<DataTypes, DataValue> = cv.get_data_container();
data_parts.push(format!("{} {}", key, val_string));
}
}
let formated_data =
format_data_container(data.iter().map(|(k, v)| (k.clone(), v.clone())).collect());
if !data_parts.is_empty() {
parts.push(format!("{}", data_parts.join(", ")));
}
parts.push(format!("{}", formated_data));
parts.join(": ")
}
fn format_data_container(data: Vec<(DataTypes, DataValue)>) -> String {
let parts: Vec<String> = data
.into_iter()
.map(|(key, value)| {
let key_str = key.to_string();
match value {
DataValue::Str(s) => format!("{}=\"{}\"", key_str, s),
DataValue::Container(inner) => {
let inner_formatted = format_data_container(inner);
format!("{}={{ {} }}", key_str, inner_formatted)
}
DataValue::Array(arr) => {
let arr_formatted = format_array(arr);
format!("{}=[{}]", key_str, arr_formatted)
}
DataValue::Bool(b) => format!("{}={}", key_str, b),
DataValue::BoolTrue => format!("{}=true", key_str),
DataValue::BoolFalse => format!("{}=false", key_str),
DataValue::Number(num) => format!("{}={}", key_str, num),
_ => "".to_string(),
}
})
.collect();
parts.join(", ")
}
fn format_array(arr: Vec<DataValue>) -> String {
let parts: Vec<String> = arr
.into_iter()
.map(|value| match value {
DataValue::Str(s) => format!("\"{}\"", s),
DataValue::Container(inner) => {
let inner_formatted = format_data_container(inner);
format!("{{ {} }}", inner_formatted)
}
DataValue::Array(inner_arr) => {
let formatted = format_array(inner_arr);
format!("[{}]", formatted)
}
DataValue::Bool(b) => b.to_string(),
DataValue::BoolTrue => "true".to_string(),
DataValue::BoolFalse => "false".to_string(),
DataValue::Number(num) => num.to_string(),
_ => String::new(),
})
.collect();
parts.join(", ")
}
#[macro_export]
macro_rules! log_cv {
($kind:expr, $cv:expr) => {

View file

@ -1,3 +1,4 @@
pub mod crypto_helper;
pub mod crypto_util;
pub mod file_util;
pub mod logger;