server side preparation
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
use crate::ClientEvent;
|
use crate::ClientEvent;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use openworm::net::{
|
use openworm::net::{
|
||||||
ClientMsg, ClientRequestMsg, RecvHandler, RequestId, RequestMsg, SERVER_NAME, ServerMsg,
|
ClientMsg, ClientMsgInst, RecvHandler, RequestId, RequestMsg, SERVER_NAME, ServerMsg,
|
||||||
ServerRespMsg, SkipServerVerification, recv_uni, send_uni,
|
ServerMsgInst, SkipServerVerification, recv_uni, send_uni,
|
||||||
};
|
};
|
||||||
use quinn::{
|
use quinn::{
|
||||||
ClientConfig, Connection, Endpoint, IdleTimeout, TransportConfig,
|
ClientConfig, Connection, Endpoint, IdleTimeout, TransportConfig,
|
||||||
@@ -10,12 +10,10 @@ use quinn::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
net::{Ipv6Addr, SocketAddr, SocketAddrV6, ToSocketAddrs},
|
net::{Ipv6Addr, SocketAddr, SocketAddrV6, ToSocketAddrs},
|
||||||
str::FromStr,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
||||||
use winit::event_loop::EventLoopProxy;
|
|
||||||
|
|
||||||
pub const CLIENT_SOCKET: SocketAddr =
|
pub const CLIENT_SOCKET: SocketAddr =
|
||||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0));
|
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0));
|
||||||
@@ -30,21 +28,8 @@ pub struct NetHandle {
|
|||||||
send: UnboundedSender<NetCtrlMsg>,
|
send: UnboundedSender<NetCtrlMsg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct AppHandle {
|
|
||||||
pub proxy: EventLoopProxy<ClientEvent>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppHandle {
|
|
||||||
pub fn send(&self, event: ClientEvent) {
|
|
||||||
self.proxy.send_event(event).unwrap_or_else(|_| panic!());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetResult<T> = Result<T, String>;
|
type NetResult<T> = Result<T, String>;
|
||||||
|
|
||||||
pub trait ClientRequest {}
|
|
||||||
|
|
||||||
pub enum NetCtrlMsg {
|
pub enum NetCtrlMsg {
|
||||||
Send(ClientMsg),
|
Send(ClientMsg),
|
||||||
Request(ClientMsg, oneshot::Sender<ServerMsg>),
|
Request(ClientMsg, oneshot::Sender<ServerMsg>),
|
||||||
@@ -156,9 +141,9 @@ impl NetHandle {
|
|||||||
let request_id = req_id.next();
|
let request_id = req_id.next();
|
||||||
match msg {
|
match msg {
|
||||||
NetCtrlMsg::Send(msg) => {
|
NetCtrlMsg::Send(msg) => {
|
||||||
let msg = ClientRequestMsg {
|
let msg = ClientMsgInst {
|
||||||
id: request_id,
|
id: request_id,
|
||||||
msg: msg.into(),
|
msg,
|
||||||
};
|
};
|
||||||
if send_uni(&conn, msg).await.is_err() {
|
if send_uni(&conn, msg).await.is_err() {
|
||||||
println!("disconnected from server");
|
println!("disconnected from server");
|
||||||
@@ -166,9 +151,9 @@ impl NetHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NetCtrlMsg::Request(msg, send) => {
|
NetCtrlMsg::Request(msg, send) => {
|
||||||
let msg = ClientRequestMsg {
|
let msg = ClientMsgInst {
|
||||||
id: request_id,
|
id: request_id,
|
||||||
msg: msg.into(),
|
msg,
|
||||||
};
|
};
|
||||||
recv.requests.insert(request_id, send);
|
recv.requests.insert(request_id, send);
|
||||||
if send_uni(&conn, msg).await.is_err() {
|
if send_uni(&conn, msg).await.is_err() {
|
||||||
@@ -206,15 +191,14 @@ struct ServerRecv<F: MsgHandler> {
|
|||||||
msg: F,
|
msg: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: MsgHandler> RecvHandler<ServerRespMsg> for ServerRecv<F> {
|
impl<F: MsgHandler> RecvHandler<ServerMsgInst> for ServerRecv<F> {
|
||||||
async fn msg(&self, resp: ServerRespMsg) {
|
async fn msg(&self, resp: ServerMsgInst) {
|
||||||
let msg = resp.msg.into();
|
|
||||||
if let Some(id) = resp.id
|
if let Some(id) = resp.id
|
||||||
&& let Some((_, send)) = self.requests.remove(&id)
|
&& let Some((_, send)) = self.requests.remove(&id)
|
||||||
{
|
{
|
||||||
send.send(msg);
|
let _ = send.send(resp.msg);
|
||||||
} else {
|
} else {
|
||||||
self.msg.run(msg).await;
|
self.msg.run(resp.msg).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/bin/client/ui/friends.rs
Normal file
12
src/bin/client/ui/friends.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// pub fn view(rsc: &mut Rsc, session: &Session) -> StrongWidget {
|
||||||
|
// let mut view = WidgetSelector::new(View::Info, info(rsc));
|
||||||
|
// view.set(View::User, users(rsc, session));
|
||||||
|
// let view = view.add(rsc);
|
||||||
|
// let [info, server] = tabs(rsc, view, [("info", View::Info), ("users", View::User)]);
|
||||||
|
//
|
||||||
|
// let side_bar = rect(Color::BLACK.alpha(150))
|
||||||
|
// .foreground((info, server).span(Dir::DOWN))
|
||||||
|
// .width(260);
|
||||||
|
//
|
||||||
|
// (side_bar, view).span(Dir::RIGHT).add_strong(rsc)
|
||||||
|
// }
|
||||||
@@ -4,6 +4,7 @@ use iris::prelude::*;
|
|||||||
mod channel;
|
mod channel;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
mod connect;
|
mod connect;
|
||||||
|
mod friends;
|
||||||
mod main;
|
mod main;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod server;
|
mod server;
|
||||||
|
|||||||
48
src/bin/server/db/data.rs
Normal file
48
src/bin/server/db/data.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use iris::core::util::HashSet;
|
||||||
|
pub use openworm::net::{AccountToken, ServerPerms};
|
||||||
|
|
||||||
|
pub type UserId = u64;
|
||||||
|
pub type MsgId = i128;
|
||||||
|
pub type ChannelId = u64;
|
||||||
|
pub type ImageId = u64;
|
||||||
|
|
||||||
|
#[derive(bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct User {
|
||||||
|
pub username: String,
|
||||||
|
pub password_hash: String,
|
||||||
|
pub pfp: Option<ImageId>,
|
||||||
|
pub bio: String,
|
||||||
|
pub friends: Friends,
|
||||||
|
pub server_perms: ServerPerms,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
pub fn new(username: String, password_hash: String, perms: ServerPerms) -> Self {
|
||||||
|
Self {
|
||||||
|
username,
|
||||||
|
password_hash,
|
||||||
|
bio: String::new(),
|
||||||
|
pfp: None,
|
||||||
|
friends: Default::default(),
|
||||||
|
server_perms: perms,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct Friends {
|
||||||
|
pub current: HashSet<UserId>,
|
||||||
|
pub outgoing: HashSet<UserId>,
|
||||||
|
pub incoming: HashSet<UserId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct Msg {
|
||||||
|
pub content: String,
|
||||||
|
pub author: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Channel {
|
||||||
|
pub name: String,
|
||||||
|
pub desc: String,
|
||||||
|
}
|
||||||
@@ -1,31 +1,22 @@
|
|||||||
|
mod data;
|
||||||
mod util;
|
mod util;
|
||||||
mod ver;
|
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub use data::*;
|
||||||
use util::*;
|
use util::*;
|
||||||
use ver::*;
|
|
||||||
|
|
||||||
pub const DB_VERSION: u64 = 0;
|
pub const DB_VERSION: u64 = 0;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Db {
|
pub struct Db {
|
||||||
db: Database,
|
db: Database,
|
||||||
pub account_tokens: DbMap<String, ServerPerms>,
|
pub account_tokens: DbMap<AccountToken, ServerPerms>,
|
||||||
pub msgs: DbMap<MsgId, Msg>,
|
pub msgs: DbMap<MsgId, Msg>,
|
||||||
pub users: DbMap<UserId, User>,
|
pub users: DbMap<UserId, User>,
|
||||||
pub usernames: DbMap<String, UserId>,
|
pub usernames: DbMap<String, UserId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UserId = UserIdV0;
|
|
||||||
pub type MsgId = MsgIdV0;
|
|
||||||
pub type ChannelId = ChannelIdV0;
|
|
||||||
pub type ImageId = ImageIdV0;
|
|
||||||
pub type User = UserV0;
|
|
||||||
pub type Msg = MsgV0;
|
|
||||||
pub type ChannelInfo = ChannelV0;
|
|
||||||
pub type ServerPerms = ServerPermsV0;
|
|
||||||
|
|
||||||
impl Db {
|
impl Db {
|
||||||
pub fn open(path: impl AsRef<Path>) -> Db {
|
pub fn open(path: impl AsRef<Path>) -> Db {
|
||||||
let db = Database::open(path);
|
let db = Database::open(path);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl<K, V> Clone for DbMap<K, V> {
|
|||||||
Self {
|
Self {
|
||||||
db: self.db.clone(),
|
db: self.db.clone(),
|
||||||
keyspace: self.keyspace.clone(),
|
keyspace: self.keyspace.clone(),
|
||||||
_pd: self._pd.clone(),
|
_pd: self._pd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ impl WriteTx {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<K: Key<Output = K>, V: DecodeOwned>(&self, map: &DbMap<K, V>, k: K) -> Option<V> {
|
pub fn get<K: Key<Output = K>, V: DecodeOwned>(&self, map: &DbMap<K, V>, k: &K) -> Option<V> {
|
||||||
let k = Slice::new(k.to_bytes().as_ref());
|
let k = Slice::new(k.to_bytes().as_ref());
|
||||||
let v = self.0.get(&map.keyspace, k).unwrap()?;
|
let v = self.0.get(&map.keyspace, k).unwrap()?;
|
||||||
Some(bitcode::decode(&v).unwrap())
|
Some(bitcode::decode(&v).unwrap())
|
||||||
@@ -137,7 +137,11 @@ impl WriteTx {
|
|||||||
self.0.get(&map.keyspace, k).unwrap().is_some()
|
self.0.get(&map.keyspace, k).unwrap().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove<K: Key<Output = K>, V: DecodeOwned>(&mut self, map: &DbMap<K, V>, k: K) -> Option<V> {
|
pub fn remove<K: Key<Output = K>, V: DecodeOwned>(
|
||||||
|
&mut self,
|
||||||
|
map: &DbMap<K, V>,
|
||||||
|
k: K,
|
||||||
|
) -> Option<V> {
|
||||||
let k = Slice::new(k.to_bytes().as_ref());
|
let k = Slice::new(k.to_bytes().as_ref());
|
||||||
let v = self.0.take(&map.keyspace, k).unwrap()?;
|
let v = self.0.take(&map.keyspace, k).unwrap()?;
|
||||||
Some(bitcode::decode(&v).unwrap())
|
Some(bitcode::decode(&v).unwrap())
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
pub type UserIdV0 = u64;
|
|
||||||
pub type MsgIdV0 = i128;
|
|
||||||
pub type ChannelIdV0 = u64;
|
|
||||||
pub type ImageIdV0 = u64;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, bitcode::Encode, bitcode::Decode)]
|
|
||||||
pub struct ServerPermsV0(u32);
|
|
||||||
impl ServerPermsV0 {
|
|
||||||
pub const NONE: Self = Self(0);
|
|
||||||
pub const ACCOUNT_TOKENS: Self = Self(1 << 0);
|
|
||||||
pub const ALL: Self = Self(u32::MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(bitcode::Encode, bitcode::Decode)]
|
|
||||||
pub struct UserV0 {
|
|
||||||
pub username: String,
|
|
||||||
pub password_hash: String,
|
|
||||||
pub pfp: Option<ImageIdV0>,
|
|
||||||
pub bio: String,
|
|
||||||
pub server_perms: ServerPermsV0,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(bitcode::Encode, bitcode::Decode)]
|
|
||||||
pub struct MsgV0 {
|
|
||||||
pub content: String,
|
|
||||||
pub author: UserIdV0,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ChannelV0 {
|
|
||||||
pub name: String,
|
|
||||||
pub desc: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServerPermsV0 {
|
|
||||||
pub fn contains(&self, other: Self) -> bool {
|
|
||||||
(self.0 & other.0) == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
313
src/bin/server/handle.rs
Normal file
313
src/bin/server/handle.rs
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
use super::net::ClientSender;
|
||||||
|
use crate::{
|
||||||
|
ClientId, account_token,
|
||||||
|
db::{Db, ServerPerms, User, UserId},
|
||||||
|
net::ClientReplier,
|
||||||
|
};
|
||||||
|
use openworm::net::*;
|
||||||
|
use scrypt::{
|
||||||
|
Scrypt,
|
||||||
|
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum ClientState {
|
||||||
|
Login,
|
||||||
|
Authed(UserId),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClientHandler {
|
||||||
|
pub db: Db,
|
||||||
|
pub send: ClientSender,
|
||||||
|
pub senders: Arc<RwLock<HashMap<ClientId, ClientSender>>>,
|
||||||
|
pub id: ClientId,
|
||||||
|
pub state: Arc<RwLock<ClientState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientHandler {
|
||||||
|
pub async fn handle_msg(&self, msg: ClientMsg, replier: &ClientReplier) -> Option<()> {
|
||||||
|
// TODO: there are some awful edge cases where you delete an account and it's in the middle
|
||||||
|
// of a non TX transaction; make sure to use TX, maybe need another check_login closure
|
||||||
|
// fortunately these should be very rare so I'ma ignore for now
|
||||||
|
macro_rules! reply {
|
||||||
|
($msg:expr) => {
|
||||||
|
replier.send($msg).await;
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let check_login = async || {
|
||||||
|
if let ClientState::Authed(uid) = &*self.state.read().await {
|
||||||
|
Some(*uid)
|
||||||
|
} else {
|
||||||
|
reply!(NotLoggedIn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let check_user = async || {
|
||||||
|
if let ClientState::Authed(uid) = &*self.state.read().await {
|
||||||
|
if let Some(user) = self.db.users.get(uid) {
|
||||||
|
Some((*uid, user))
|
||||||
|
} else {
|
||||||
|
reply!(AccountDeleted);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply!(NotLoggedIn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let check_server_perms = async |perms| {
|
||||||
|
let id = check_login().await?;
|
||||||
|
if self
|
||||||
|
.db
|
||||||
|
.users
|
||||||
|
.get(&id)
|
||||||
|
.is_some_and(|u| !u.server_perms.contains(perms))
|
||||||
|
{
|
||||||
|
reply!(NoPermission);
|
||||||
|
} else {
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let db = &self.db;
|
||||||
|
match msg {
|
||||||
|
ClientMsg::GenerateToken(info) => {
|
||||||
|
let check = if info.perms == ServerPerms::NONE {
|
||||||
|
ServerPerms::ACCOUNT_TOKENS
|
||||||
|
} else {
|
||||||
|
ServerPerms::ALL
|
||||||
|
};
|
||||||
|
check_server_perms(check).await?;
|
||||||
|
let token = account_token(&self.db, info.perms);
|
||||||
|
reply!(GenerateTokenResp { token });
|
||||||
|
}
|
||||||
|
ClientMsg::CreateAccount(info) => {
|
||||||
|
let CreateAccount {
|
||||||
|
token,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
} = &info;
|
||||||
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
|
let params = scrypt::Params::new(11, 8, 1, 32).unwrap();
|
||||||
|
let hash = Scrypt
|
||||||
|
.hash_password_customized(password.as_bytes(), None, None, params, &salt)
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
let mut id;
|
||||||
|
loop {
|
||||||
|
let mut tx = db.write_tx();
|
||||||
|
let Some(perms) = tx.remove(&db.account_tokens, token.to_string()) else {
|
||||||
|
println!("invalid token: {:?}", self.send.remote());
|
||||||
|
reply!(CreateAccountResp::InvalidToken);
|
||||||
|
};
|
||||||
|
if tx.has_key(&db.usernames, username.clone()) {
|
||||||
|
reply!(CreateAccountResp::UsernameExists);
|
||||||
|
}
|
||||||
|
id = rand::random();
|
||||||
|
while tx.has_key(&db.users, id) {
|
||||||
|
id = rand::random();
|
||||||
|
}
|
||||||
|
tx.insert(
|
||||||
|
&db.users,
|
||||||
|
&id,
|
||||||
|
&User::new(username.clone(), hash.clone(), perms),
|
||||||
|
);
|
||||||
|
tx.insert(&db.usernames, username, &id);
|
||||||
|
if tx.commit() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("account created: \"{username}\"");
|
||||||
|
*self.state.write().await = ClientState::Authed(id);
|
||||||
|
reply!(CreateAccountResp::Ok { id });
|
||||||
|
}
|
||||||
|
ClientMsg::Login(info) => {
|
||||||
|
let Login { username, password } = &info;
|
||||||
|
let Some(id) = db.usernames.get(username) else {
|
||||||
|
reply!(LoginResp::UnknownUsername);
|
||||||
|
};
|
||||||
|
let Some(user) = db.users.get(&id) else {
|
||||||
|
panic!("invalid state! (should be a user)");
|
||||||
|
};
|
||||||
|
let hash = PasswordHash::new(&user.password_hash).unwrap();
|
||||||
|
if Scrypt.verify_password(password.as_bytes(), &hash).is_err() {
|
||||||
|
println!("invalid password: \"{username}\"");
|
||||||
|
reply!(LoginResp::InvalidPassword);
|
||||||
|
}
|
||||||
|
*self.state.write().await = ClientState::Authed(id);
|
||||||
|
reply!(LoginResp::Ok { id });
|
||||||
|
}
|
||||||
|
ClientMsg::RequestUsers(_) => {
|
||||||
|
check_login().await?;
|
||||||
|
check_server_perms(ServerPerms::ALL).await?;
|
||||||
|
let users: Vec<_> = self
|
||||||
|
.db
|
||||||
|
.users
|
||||||
|
.iter()
|
||||||
|
.map(|(id, u)| ServerUser {
|
||||||
|
id,
|
||||||
|
username: u.username,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
reply!(RequestUsersResp { users });
|
||||||
|
}
|
||||||
|
ClientMsg::AddFriend(info) => {
|
||||||
|
let user_id = check_login().await?;
|
||||||
|
loop {
|
||||||
|
let mut tx = db.write_tx();
|
||||||
|
let Some(mut user) = tx.get(&db.users, &user_id) else {
|
||||||
|
reply!(AccountDeleted);
|
||||||
|
};
|
||||||
|
let Some(friend_id) = tx.get(&db.usernames, &info.username) else {
|
||||||
|
reply!(AddFriendResp::UnknownUser);
|
||||||
|
};
|
||||||
|
if user.friends.outgoing.contains(&friend_id) {
|
||||||
|
reply!(AddFriendResp::AlreadySent);
|
||||||
|
}
|
||||||
|
if friend_id == user_id {
|
||||||
|
reply!(AddFriendResp::CannotAddSelf);
|
||||||
|
}
|
||||||
|
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
||||||
|
println!("WARNING: username without valid user!");
|
||||||
|
reply!(AddFriendResp::UnknownUser);
|
||||||
|
};
|
||||||
|
if other.friends.current.contains(&user_id) {
|
||||||
|
reply!(AddFriendResp::AlreadyFriends);
|
||||||
|
}
|
||||||
|
user.friends.outgoing.insert(friend_id);
|
||||||
|
other.friends.incoming.insert(user_id);
|
||||||
|
tx.insert(&db.users, &user_id, &user);
|
||||||
|
tx.insert(&db.users, &friend_id, &other);
|
||||||
|
if tx.commit() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply!(AddFriendResp::Ok);
|
||||||
|
}
|
||||||
|
ClientMsg::RemoveFriend(info) => {
|
||||||
|
let user_id = check_login().await?;
|
||||||
|
loop {
|
||||||
|
let mut tx = db.write_tx();
|
||||||
|
let mut user = tx.get(&db.users, &user_id)?;
|
||||||
|
let friend_id = info.id;
|
||||||
|
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
||||||
|
println!("WARNING: username without valid user!");
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
user.friends.current.remove(&friend_id);
|
||||||
|
other.friends.current.remove(&user_id);
|
||||||
|
tx.insert(&db.users, &user_id, &user);
|
||||||
|
tx.insert(&db.users, &friend_id, &other);
|
||||||
|
if tx.commit() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply!(AddFriendResp::Ok);
|
||||||
|
}
|
||||||
|
ClientMsg::RequestFriends(_) => {
|
||||||
|
let user = check_user().await?.1;
|
||||||
|
reply!(RequestFriendsResp {
|
||||||
|
current: user.friends.current,
|
||||||
|
incoming: user.friends.incoming,
|
||||||
|
outgoing: user.friends.outgoing,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ClientMsg::AnswerFriendRequest(answer) => {
|
||||||
|
let user_id = check_login().await?;
|
||||||
|
loop {
|
||||||
|
let mut tx = db.write_tx();
|
||||||
|
let mut user = tx.get(&db.users, &user_id)?;
|
||||||
|
let other_id = answer.id;
|
||||||
|
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
||||||
|
println!("WARNING: username without valid user!");
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
match answer.action {
|
||||||
|
FriendRequestAction::Accept => {
|
||||||
|
user.friends.incoming.remove(&other_id);
|
||||||
|
other.friends.outgoing.remove(&user_id);
|
||||||
|
user.friends.current.insert(other_id);
|
||||||
|
other.friends.current.insert(user_id);
|
||||||
|
}
|
||||||
|
FriendRequestAction::Deny => {
|
||||||
|
user.friends.incoming.remove(&other_id);
|
||||||
|
other.friends.outgoing.remove(&user_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx.insert(&db.users, &user_id, &user);
|
||||||
|
tx.insert(&db.users, &other_id, &other);
|
||||||
|
if tx.commit() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecvHandler<ClientMsgInst> for ClientHandler {
|
||||||
|
async fn connect(&self) -> () {}
|
||||||
|
async fn msg(&self, req: ClientMsgInst) {
|
||||||
|
let replier = self.send.replier(req.id);
|
||||||
|
self.handle_msg(req.msg, &replier).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn disconnect(&self, reason: DisconnectReason) -> () {
|
||||||
|
match reason {
|
||||||
|
DisconnectReason::Closed | DisconnectReason::Timeout => (),
|
||||||
|
DisconnectReason::Other(e) => println!("connection issue: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientMsg::SendMsg(msg) => {
|
||||||
|
// let uid = check_login().await?;
|
||||||
|
// let msg = Msg {
|
||||||
|
// author: uid,
|
||||||
|
// content: msg.content,
|
||||||
|
// };
|
||||||
|
// // TODO: it is technically possible to send 2 messages at the exact same time...
|
||||||
|
// // should probably append a number if one already exists at that time,
|
||||||
|
// // but also I can't see this ever happening...?
|
||||||
|
// // should be an easy fix later (write tx)
|
||||||
|
// let timestamp = time::OffsetDateTime::now_utc().unix_timestamp_nanos();
|
||||||
|
// self.db.msgs.insert(×tamp, &msg);
|
||||||
|
// let mut handles = Vec::new();
|
||||||
|
// let msg = LoadMsg {
|
||||||
|
// content: msg.content,
|
||||||
|
// author: uid,
|
||||||
|
// };
|
||||||
|
// for (&id, send) in self.senders.read().await.iter() {
|
||||||
|
// if id == self.id {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// let send = send.clone();
|
||||||
|
// let msg = msg.clone();
|
||||||
|
// let fut = async move {
|
||||||
|
// let _ = send
|
||||||
|
// .send(LoadMsg {
|
||||||
|
// content: msg.content,
|
||||||
|
// author: msg.author,
|
||||||
|
// })
|
||||||
|
// .await;
|
||||||
|
// };
|
||||||
|
// handles.push(tokio::spawn(fut));
|
||||||
|
// }
|
||||||
|
// for h in handles {
|
||||||
|
// h.await.unwrap();
|
||||||
|
// }
|
||||||
|
// None
|
||||||
|
// }
|
||||||
|
// ClientMsg::RequestMsgs => {
|
||||||
|
// check_login().await?;
|
||||||
|
// let msgs = self
|
||||||
|
// .db
|
||||||
|
// .msgs
|
||||||
|
// .values()
|
||||||
|
// .map(|msg| LoadMsg {
|
||||||
|
// content: msg.content,
|
||||||
|
// author: msg.author,
|
||||||
|
// })
|
||||||
|
// .collect();
|
||||||
|
// replier.send(ServerMsg::LoadMsgs(msgs)).await;
|
||||||
|
// }
|
||||||
@@ -1,22 +1,18 @@
|
|||||||
mod db;
|
mod db;
|
||||||
|
mod handle;
|
||||||
mod net;
|
mod net;
|
||||||
|
|
||||||
use crate::db::{Db, Msg, ServerPerms, User};
|
use crate::{
|
||||||
|
db::{Db, ServerPerms},
|
||||||
|
handle::{ClientHandler, ClientState},
|
||||||
|
};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use net::{ClientSender, ConAccepter, listen};
|
use net::{ClientSender, ConAccepter, listen};
|
||||||
use openworm::{
|
use openworm::{
|
||||||
net::{
|
net::{AccountToken, ClientMsgInst, RecvHandler, install_crypto_provider},
|
||||||
ClientMsg, ClientRequestMsg, CreateAccount, CreateAccountResp, DisconnectReason, LoadMsg,
|
|
||||||
Login, LoginResp, RecvHandler, RequestUsersResp, ServerError, ServerMsg, ServerUser,
|
|
||||||
install_crypto_provider,
|
|
||||||
},
|
|
||||||
rsc::DataDir,
|
rsc::DataDir,
|
||||||
};
|
};
|
||||||
use rand::distr::{Alphanumeric, SampleString};
|
use rand::distr::{Alphanumeric, SampleString};
|
||||||
use scrypt::{
|
|
||||||
Scrypt,
|
|
||||||
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::{
|
sync::{
|
||||||
@@ -62,7 +58,7 @@ pub async fn run_server(port: u16) {
|
|||||||
endpoint.wait_idle().await;
|
endpoint.wait_idle().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn account_token(db: &Db, perms: ServerPerms) -> String {
|
pub fn account_token(db: &Db, perms: ServerPerms) -> AccountToken {
|
||||||
let token = Alphanumeric.sample_string(&mut rand::rng(), 16);
|
let token = Alphanumeric.sample_string(&mut rand::rng(), 16);
|
||||||
db.account_tokens.insert(&token, &perms);
|
db.account_tokens.insert(&token, &perms);
|
||||||
token
|
token
|
||||||
@@ -76,14 +72,8 @@ struct ServerListener {
|
|||||||
count: AtomicU64,
|
count: AtomicU64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum ClientState {
|
|
||||||
Login,
|
|
||||||
Authed(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConAccepter for ServerListener {
|
impl ConAccepter for ServerListener {
|
||||||
async fn accept(&self, send: ClientSender) -> impl RecvHandler<ClientRequestMsg> {
|
async fn accept(&self, send: ClientSender) -> impl RecvHandler<ClientMsgInst> {
|
||||||
let id = self.count.fetch_add(1, Ordering::Release);
|
let id = self.count.fetch_add(1, Ordering::Release);
|
||||||
self.senders.write().await.insert(id, send.clone());
|
self.senders.write().await.insert(id, send.clone());
|
||||||
ClientHandler {
|
ClientHandler {
|
||||||
@@ -95,177 +85,3 @@ impl ConAccepter for ServerListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ClientHandler {
|
|
||||||
db: Db,
|
|
||||||
send: ClientSender,
|
|
||||||
senders: Arc<RwLock<HashMap<ClientId, ClientSender>>>,
|
|
||||||
id: ClientId,
|
|
||||||
state: Arc<RwLock<ClientState>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecvHandler<ClientRequestMsg> for ClientHandler {
|
|
||||||
async fn connect(&self) -> () {}
|
|
||||||
async fn msg(&self, req: ClientRequestMsg) {
|
|
||||||
let msg = ClientMsg::from(req.msg);
|
|
||||||
let replier = self.send.replier(req.id);
|
|
||||||
match msg {
|
|
||||||
ClientMsg::SendMsg(msg) => {
|
|
||||||
let ClientState::Authed(uid) = &*self.state.read().await else {
|
|
||||||
let _ = replier.send(ServerError::NotLoggedIn).await;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let msg = Msg {
|
|
||||||
author: *uid,
|
|
||||||
content: msg.content,
|
|
||||||
};
|
|
||||||
// TODO: it is technically possible to send 2 messages at the exact same time...
|
|
||||||
// should probably append a number if one already exists at that time,
|
|
||||||
// but also I can't see this ever happening...?
|
|
||||||
// should be an easy fix later (write tx)
|
|
||||||
let timestamp = time::OffsetDateTime::now_utc().unix_timestamp_nanos();
|
|
||||||
self.db.msgs.insert(×tamp, &msg);
|
|
||||||
let mut handles = Vec::new();
|
|
||||||
let msg = LoadMsg {
|
|
||||||
content: msg.content,
|
|
||||||
author: *uid,
|
|
||||||
};
|
|
||||||
for (&id, send) in self.senders.read().await.iter() {
|
|
||||||
if id == self.id {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let send = send.clone();
|
|
||||||
let msg = msg.clone();
|
|
||||||
let fut = async move {
|
|
||||||
let _ = send
|
|
||||||
.send(LoadMsg {
|
|
||||||
content: msg.content,
|
|
||||||
author: msg.author,
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
};
|
|
||||||
handles.push(tokio::spawn(fut));
|
|
||||||
}
|
|
||||||
for h in handles {
|
|
||||||
h.await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientMsg::RequestMsgs => {
|
|
||||||
let ClientState::Authed(_uid) = &*self.state.read().await else {
|
|
||||||
let _ = replier.send(ServerError::NotLoggedIn).await;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let msgs = self
|
|
||||||
.db
|
|
||||||
.msgs
|
|
||||||
.values()
|
|
||||||
.map(|msg| LoadMsg {
|
|
||||||
content: msg.content,
|
|
||||||
author: msg.author,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let _ = replier.send(ServerMsg::LoadMsgs(msgs)).await;
|
|
||||||
}
|
|
||||||
ClientMsg::CreateAccount(info) => {
|
|
||||||
let CreateAccount {
|
|
||||||
token,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
} = &info;
|
|
||||||
let salt = SaltString::generate(&mut OsRng);
|
|
||||||
let params = scrypt::Params::new(11, 8, 1, 32).unwrap();
|
|
||||||
let hash = Scrypt
|
|
||||||
.hash_password_customized(password.as_bytes(), None, None, params, &salt)
|
|
||||||
.unwrap()
|
|
||||||
.to_string();
|
|
||||||
let mut id;
|
|
||||||
loop {
|
|
||||||
let mut tx = self.db.write_tx();
|
|
||||||
let Some(perms) = tx.remove(&self.db.account_tokens, token.to_string()) else {
|
|
||||||
let _ = replier.send(CreateAccountResp::InvalidToken).await;
|
|
||||||
println!("invalid token: {:?}", self.send.remote());
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if tx.has_key(&self.db.usernames, username.clone()) {
|
|
||||||
let _ = replier.send(CreateAccountResp::UsernameExists).await;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
id = rand::random();
|
|
||||||
while tx.has_key(&self.db.users, id) {
|
|
||||||
id = rand::random();
|
|
||||||
}
|
|
||||||
tx.insert(
|
|
||||||
&self.db.users,
|
|
||||||
&id,
|
|
||||||
&User {
|
|
||||||
username: username.clone(),
|
|
||||||
password_hash: hash.clone(),
|
|
||||||
bio: String::new(),
|
|
||||||
pfp: None,
|
|
||||||
server_perms: perms,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
tx.insert(&self.db.usernames, username, &id);
|
|
||||||
if tx.commit() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("account created: \"{username}\"");
|
|
||||||
*self.state.write().await = ClientState::Authed(id);
|
|
||||||
let _ = replier.send(CreateAccountResp::Ok { id }).await;
|
|
||||||
}
|
|
||||||
ClientMsg::Login(info) => {
|
|
||||||
let Login { username, password } = &info;
|
|
||||||
let Some(id) = self.db.usernames.get(username) else {
|
|
||||||
let _ = replier.send(LoginResp::UnknownUsername).await;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(user) = self.db.users.get(&id) else {
|
|
||||||
panic!("invalid state! (should be a user)");
|
|
||||||
};
|
|
||||||
let hash = PasswordHash::new(&user.password_hash).unwrap();
|
|
||||||
if Scrypt.verify_password(password.as_bytes(), &hash).is_err() {
|
|
||||||
println!("invalid password: \"{username}\"");
|
|
||||||
let _ = replier.send(LoginResp::InvalidPassword).await;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*self.state.write().await = ClientState::Authed(id);
|
|
||||||
let _ = replier.send(LoginResp::Ok { id }).await;
|
|
||||||
}
|
|
||||||
ClientMsg::RequestUsers(_) => {
|
|
||||||
if self
|
|
||||||
.db
|
|
||||||
.users
|
|
||||||
.get(&self.id)
|
|
||||||
.is_some_and(|u| !u.server_perms.contains(ServerPerms::ALL))
|
|
||||||
{
|
|
||||||
let _ = replier.send(ServerError::NoPermission).await;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let users: Vec<_> = self
|
|
||||||
.db
|
|
||||||
.users
|
|
||||||
.iter()
|
|
||||||
.map(|(id, u)| ServerUser {
|
|
||||||
id,
|
|
||||||
username: u.username,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let _ = replier.send(RequestUsersResp { users }).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn disconnect(&self, reason: DisconnectReason) -> () {
|
|
||||||
match reason {
|
|
||||||
DisconnectReason::Closed | DisconnectReason::Timeout => (),
|
|
||||||
DisconnectReason::Other(e) => println!("connection issue: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientState {
|
|
||||||
pub fn is_authed(&self) -> bool {
|
|
||||||
matches!(self, Self::Authed(_))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use openworm::net::{
|
use openworm::net::{
|
||||||
ClientRequestMsg, RecvHandler, RequestId, SERVER_NAME, SendResult, ServerMsg, ServerRespMsg,
|
ClientMsgInst, RecvHandler, RequestId, SERVER_NAME, SendResult, ServerMsg, ServerMsgInst,
|
||||||
recv_uni, send_uni,
|
recv_uni, send_uni,
|
||||||
};
|
};
|
||||||
use quinn::{
|
use quinn::{
|
||||||
@@ -65,9 +65,9 @@ impl ClientSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(&self, msg: impl Into<ServerMsg>) -> SendResult {
|
pub async fn send(&self, msg: impl Into<ServerMsg>) -> SendResult {
|
||||||
let msg = ServerRespMsg {
|
let msg = ServerMsgInst {
|
||||||
id: None,
|
id: None,
|
||||||
msg: msg.into().into(),
|
msg: msg.into(),
|
||||||
};
|
};
|
||||||
send_uni(&self.conn, msg).await
|
send_uni(&self.conn, msg).await
|
||||||
}
|
}
|
||||||
@@ -79,12 +79,12 @@ pub struct ClientReplier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientReplier {
|
impl ClientReplier {
|
||||||
pub async fn send(&self, msg: impl Into<ServerMsg>) -> SendResult {
|
pub async fn send(&self, msg: impl Into<ServerMsg>) {
|
||||||
let msg = ServerRespMsg {
|
let msg = ServerMsgInst {
|
||||||
id: Some(self.req_id),
|
id: Some(self.req_id),
|
||||||
msg: msg.into().into(),
|
msg: msg.into(),
|
||||||
};
|
};
|
||||||
send_uni(&self.conn, msg).await
|
let _ = send_uni(&self.conn, msg).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ pub trait ConAccepter: Send + Sync + 'static {
|
|||||||
fn accept(
|
fn accept(
|
||||||
&self,
|
&self,
|
||||||
send: ClientSender,
|
send: ClientSender,
|
||||||
) -> impl Future<Output = impl RecvHandler<ClientRequestMsg>> + Send;
|
) -> impl Future<Output = impl RecvHandler<ClientMsgInst>> + Send;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen(
|
pub fn listen(
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
use crate::net::{
|
|
||||||
ClientMsg, ServerMsg,
|
|
||||||
data::{ClientMsgInst, ServerMsgInst},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl From<ClientMsg> for ClientMsgInst {
|
|
||||||
fn from(value: ClientMsg) -> Self {
|
|
||||||
match value {
|
|
||||||
ClientMsg::CreateAccount(v) => Self::CreateAccountV0(v),
|
|
||||||
ClientMsg::RequestMsgs => Self::RequestMsgsV0,
|
|
||||||
ClientMsg::SendMsg(v) => Self::SendMsgV0(v),
|
|
||||||
ClientMsg::Login(v) => Self::LoginV0(v),
|
|
||||||
ClientMsg::RequestUsers(v) => Self::RequestUsersV0(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ClientMsgInst> for ClientMsg {
|
|
||||||
fn from(value: ClientMsgInst) -> Self {
|
|
||||||
match value {
|
|
||||||
ClientMsgInst::CreateAccountV0(v) => Self::CreateAccount(v),
|
|
||||||
ClientMsgInst::RequestMsgsV0 => Self::RequestMsgs,
|
|
||||||
ClientMsgInst::SendMsgV0(v) => Self::SendMsg(v),
|
|
||||||
ClientMsgInst::LoginV0(v) => Self::Login(v),
|
|
||||||
ClientMsgInst::RequestUsersV0(v) => Self::RequestUsers(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ServerMsg> for ServerMsgInst {
|
|
||||||
fn from(value: ServerMsg) -> Self {
|
|
||||||
match value {
|
|
||||||
ServerMsg::CreateAccountResp(v) => Self::CreateAccountRespV0(v),
|
|
||||||
ServerMsg::LoadMsg(v) => Self::LoadMsgV0(v),
|
|
||||||
ServerMsg::LoadMsgs(v) => Self::LoadMsgsV0(v),
|
|
||||||
ServerMsg::ServerError(v) => Self::ServerErrorV0(v),
|
|
||||||
ServerMsg::LoginResp(v) => Self::LoginRespV0(v),
|
|
||||||
ServerMsg::RequestUsersResp(v) => Self::RequestUsersRespV0(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ServerMsgInst> for ServerMsg {
|
|
||||||
fn from(value: ServerMsgInst) -> Self {
|
|
||||||
match value {
|
|
||||||
ServerMsgInst::CreateAccountRespV0(v) => Self::CreateAccountResp(v),
|
|
||||||
ServerMsgInst::LoadMsgV0(v) => Self::LoadMsg(v),
|
|
||||||
ServerMsgInst::LoadMsgsV0(v) => Self::LoadMsgs(v),
|
|
||||||
ServerMsgInst::ServerErrorV0(v) => Self::ServerError(v),
|
|
||||||
ServerMsgInst::LoginRespV0(v) => Self::LoginResp(v),
|
|
||||||
ServerMsgInst::RequestUsersRespV0(v) => Self::RequestUsersResp(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
156
src/net/data.rs
156
src/net/data.rs
@@ -1,106 +1,110 @@
|
|||||||
use rand::TryRng;
|
use iris::core::util::HashSet;
|
||||||
|
|
||||||
#[repr(u32)]
|
pub type UserId = u64;
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
pub type AccountToken = String;
|
||||||
pub enum ClientMsgInst {
|
|
||||||
CreateAccountV0(CreateAccountV0) = 0,
|
|
||||||
LoginV0(LoginV0) = 1,
|
|
||||||
RequestMsgsV0 = 2,
|
|
||||||
SendMsgV0(SendMsgV0) = 3,
|
|
||||||
RequestUsersV0(RequestUsersV0) = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u32)]
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
|
||||||
pub enum ServerMsgInst {
|
|
||||||
CreateAccountRespV0(CreateAccountRespV0) = 0,
|
|
||||||
LoginRespV0(LoginRespV0) = 1,
|
|
||||||
LoadMsgV0(LoadMsgV0) = 2,
|
|
||||||
LoadMsgsV0(Vec<LoadMsgV0>) = 3,
|
|
||||||
ServerErrorV0(ServerErrorV0) = 4,
|
|
||||||
RequestUsersRespV0(RequestUsersRespV0) = 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type UserIdV0 = u64;
|
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub struct CreateAccountV0 {
|
pub struct CreateAccount {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub struct LoginV0 {
|
pub enum CreateAccountResp {
|
||||||
pub username: String,
|
Ok { id: UserId },
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
|
||||||
pub enum CreateAccountRespV0 {
|
|
||||||
Ok { id: UserIdV0 },
|
|
||||||
UsernameExists,
|
UsernameExists,
|
||||||
InvalidToken,
|
InvalidToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub enum LoginRespV0 {
|
pub struct Login {
|
||||||
Ok { id: UserIdV0 },
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub enum LoginResp {
|
||||||
|
Ok { id: UserId },
|
||||||
UnknownUsername,
|
UnknownUsername,
|
||||||
InvalidPassword,
|
InvalidPassword,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub struct RequestUsersV0;
|
pub struct RequestUsers;
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub struct RequestUsersRespV0 {
|
pub struct RequestUsersResp {
|
||||||
pub users: Vec<ServerUserV0>,
|
pub users: Vec<ServerUser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub struct ServerUserV0 {
|
pub struct ServerUser {
|
||||||
pub id: UserIdV0,
|
pub id: UserId,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub struct LoginKeyV0(Vec<u8>);
|
pub struct RequestFriends;
|
||||||
impl LoginKeyV0 {
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub const BIT_LEN: usize = 1024;
|
pub struct RequestFriendsResp {
|
||||||
pub const BYTE_LEN: usize = Self::BIT_LEN / 8;
|
pub current: HashSet<UserId>,
|
||||||
|
pub incoming: HashSet<UserId>,
|
||||||
pub fn new() -> Self {
|
pub outgoing: HashSet<UserId>,
|
||||||
let mut key = [0u8; Self::BYTE_LEN];
|
|
||||||
rand::rngs::SysRng
|
|
||||||
.try_fill_bytes(&mut key)
|
|
||||||
.expect("failed to generate random key");
|
|
||||||
Self(key.to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bytes(&self) -> &[u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Vec<u8>> for LoginKeyV0 {
|
|
||||||
fn from(value: Vec<u8>) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, bitcode::Encode, bitcode::Decode)]
|
|
||||||
pub struct SendMsgV0 {
|
|
||||||
pub content: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, bitcode::Encode, bitcode::Decode)]
|
|
||||||
pub struct LoadMsgV0 {
|
|
||||||
pub content: String,
|
|
||||||
pub author: UserIdV0,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub enum ServerErrorV0 {
|
pub struct AddFriend {
|
||||||
NotLoggedIn,
|
pub username: String,
|
||||||
NoPermission,
|
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub enum AddFriendResp {
|
||||||
|
Ok,
|
||||||
|
UnknownUser,
|
||||||
|
CannotAddSelf,
|
||||||
|
AlreadySent,
|
||||||
|
AlreadyFriends,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct RemoveFriend {
|
||||||
|
pub id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct AnswerFriendRequest {
|
||||||
|
pub id: UserId,
|
||||||
|
pub action: FriendRequestAction,
|
||||||
|
}
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub enum FriendRequestAction {
|
||||||
|
Accept,
|
||||||
|
Deny,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct GenerateToken {
|
||||||
|
pub perms: ServerPerms,
|
||||||
|
}
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct GenerateTokenResp {
|
||||||
|
pub token: AccountToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct ServerPerms(u32);
|
||||||
|
impl ServerPerms {
|
||||||
|
pub const NONE: Self = Self(0);
|
||||||
|
pub const ACCOUNT_TOKENS: Self = Self(1 << 0);
|
||||||
|
pub const ALL: Self = Self(u32::MAX);
|
||||||
|
}
|
||||||
|
impl ServerPerms {
|
||||||
|
pub fn contains(&self, other: Self) -> bool {
|
||||||
|
(self.0 & other.0) == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct NotLoggedIn;
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct NoPermission;
|
||||||
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
|
pub struct AccountDeleted;
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
mod conversion;
|
mod data;
|
||||||
pub mod data;
|
|
||||||
mod msg;
|
mod msg;
|
||||||
mod no_cert;
|
mod no_cert;
|
||||||
mod request;
|
mod request;
|
||||||
mod transfer;
|
mod transfer;
|
||||||
|
|
||||||
pub use data::{ClientMsgInst, ServerMsgInst};
|
pub use data::*;
|
||||||
pub use msg::*;
|
pub use msg::*;
|
||||||
pub use no_cert::*;
|
pub use no_cert::*;
|
||||||
pub use request::*;
|
pub use request::*;
|
||||||
@@ -13,8 +12,6 @@ pub use transfer::*;
|
|||||||
|
|
||||||
pub const SERVER_NAME: &str = "openworm";
|
pub const SERVER_NAME: &str = "openworm";
|
||||||
|
|
||||||
pub type ServerResp<T> = Result<T, String>;
|
|
||||||
|
|
||||||
pub fn install_crypto_provider() {
|
pub fn install_crypto_provider() {
|
||||||
quinn::rustls::crypto::ring::default_provider()
|
quinn::rustls::crypto::ring::default_provider()
|
||||||
.install_default()
|
.install_default()
|
||||||
|
|||||||
132
src/net/msg.rs
132
src/net/msg.rs
@@ -1,83 +1,53 @@
|
|||||||
use super::data;
|
use super::{RequestMsg, data::*};
|
||||||
|
|
||||||
#[derive(Debug)]
|
msg_type!(ClientMsg: {
|
||||||
pub enum ClientMsg {
|
0: CreateAccount => CreateAccountResp,
|
||||||
CreateAccount(CreateAccount),
|
1: Login => LoginResp,
|
||||||
Login(Login),
|
2: RequestUsers => RequestUsersResp,
|
||||||
RequestMsgs,
|
3: RequestFriends => RequestFriendsResp,
|
||||||
SendMsg(SendMsg),
|
4: AddFriend => AddFriendResp,
|
||||||
RequestUsers(RequestUsers),
|
5: RemoveFriend,
|
||||||
}
|
6: AnswerFriendRequest,
|
||||||
|
7: GenerateToken => GenerateTokenResp,
|
||||||
#[derive(Debug)]
|
});
|
||||||
pub enum ServerMsg {
|
msg_type!(ServerMsg: {
|
||||||
CreateAccountResp(CreateAccountResp),
|
0: NotLoggedIn,
|
||||||
LoginResp(LoginResp),
|
1: NoPermission,
|
||||||
LoadMsg(LoadMsg),
|
2: AccountDeleted,
|
||||||
LoadMsgs(Vec<LoadMsg>),
|
3: CreateAccountResp,
|
||||||
ServerError(ServerError),
|
4: LoginResp,
|
||||||
RequestUsersResp(RequestUsersResp),
|
5: RequestUsersResp,
|
||||||
}
|
6: RequestFriendsResp,
|
||||||
|
7: AddFriendResp,
|
||||||
// TODO: a ton of this should really just be macros :sob:
|
8: GenerateTokenResp,
|
||||||
|
});
|
||||||
pub use data::CreateAccountRespV0 as CreateAccountResp;
|
|
||||||
pub use data::CreateAccountV0 as CreateAccount;
|
macro_rules! msg_type {
|
||||||
pub use data::LoadMsgV0 as LoadMsg;
|
($msg:ident: {$($num:literal: $name:ident $(=> $resp:ident)?,)*}) => {
|
||||||
pub use data::LoginKeyV0 as LoginKey;
|
#[repr(u32)]
|
||||||
pub use data::LoginRespV0 as LoginResp;
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub use data::LoginV0 as Login;
|
pub enum $msg {$(
|
||||||
pub use data::RequestUsersRespV0 as RequestUsersResp;
|
$name($name) = $num,
|
||||||
pub use data::RequestUsersV0 as RequestUsers;
|
)*}
|
||||||
pub use data::SendMsgV0 as SendMsg;
|
$(
|
||||||
pub use data::ServerErrorV0 as ServerError;
|
impl From<$name> for $msg {
|
||||||
pub use data::ServerUserV0 as ServerUser;
|
fn from(value: $name) -> Self {
|
||||||
pub use data::UserIdV0 as UserId;
|
Self::$name(value)
|
||||||
|
}
|
||||||
impl From<CreateAccount> for ClientMsg {
|
}
|
||||||
fn from(value: CreateAccount) -> Self {
|
$(
|
||||||
Self::CreateAccount(value)
|
impl RequestMsg for $name {
|
||||||
}
|
type Result = $resp;
|
||||||
}
|
fn result(msg: ServerMsg) -> Option<Self::Result> {
|
||||||
|
if let ServerMsg::$resp(res) = msg {
|
||||||
impl From<Login> for ClientMsg {
|
Some(res)
|
||||||
fn from(value: Login) -> Self {
|
} else {
|
||||||
Self::Login(value)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
impl From<RequestUsers> for ClientMsg {
|
)?
|
||||||
fn from(value: RequestUsers) -> Self {
|
)*
|
||||||
Self::RequestUsers(value)
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ServerError> for ServerMsg {
|
|
||||||
fn from(value: ServerError) -> Self {
|
|
||||||
Self::ServerError(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LoadMsg> for ServerMsg {
|
|
||||||
fn from(value: LoadMsg) -> Self {
|
|
||||||
Self::LoadMsg(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CreateAccountResp> for ServerMsg {
|
|
||||||
fn from(value: CreateAccountResp) -> Self {
|
|
||||||
Self::CreateAccountResp(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LoginResp> for ServerMsg {
|
|
||||||
fn from(value: LoginResp) -> Self {
|
|
||||||
Self::LoginResp(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RequestUsersResp> for ServerMsg {
|
|
||||||
fn from(value: RequestUsersResp) -> Self {
|
|
||||||
Self::RequestUsersResp(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
use msg_type;
|
||||||
|
|||||||
@@ -19,54 +19,18 @@ impl RequestId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub struct ClientRequestMsg {
|
pub struct ClientMsgInst {
|
||||||
pub id: RequestId,
|
pub id: RequestId,
|
||||||
pub msg: ClientMsgInst,
|
pub msg: ClientMsg,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
|
||||||
pub struct ServerRespMsg {
|
pub struct ServerMsgInst {
|
||||||
pub id: Option<RequestId>,
|
pub id: Option<RequestId>,
|
||||||
pub msg: ServerMsgInst,
|
pub msg: ServerMsg,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RequestMsg: Into<ClientMsg> {
|
pub trait RequestMsg: Into<ClientMsg> {
|
||||||
type Result;
|
type Result;
|
||||||
fn result(msg: ServerMsg) -> Option<Self::Result>;
|
fn result(msg: ServerMsg) -> Option<Self::Result>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RequestMsg for CreateAccount {
|
|
||||||
type Result = CreateAccountResp;
|
|
||||||
|
|
||||||
fn result(msg: ServerMsg) -> Option<Self::Result> {
|
|
||||||
if let ServerMsg::CreateAccountResp(res) = msg {
|
|
||||||
Some(res)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequestMsg for Login {
|
|
||||||
type Result = LoginResp;
|
|
||||||
|
|
||||||
fn result(msg: ServerMsg) -> Option<Self::Result> {
|
|
||||||
if let ServerMsg::LoginResp(res) = msg {
|
|
||||||
Some(res)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequestMsg for RequestUsers {
|
|
||||||
type Result = RequestUsersResp;
|
|
||||||
|
|
||||||
fn result(msg: ServerMsg) -> Option<Self::Result> {
|
|
||||||
if let ServerMsg::RequestUsersResp(res) = msg {
|
|
||||||
Some(res)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user