diff options
| -rw-r--r-- | mumctl/src/main.rs | 143 | ||||
| -rw-r--r-- | mumd/src/command.rs | 5 | ||||
| -rw-r--r-- | mumd/src/main.rs | 41 | ||||
| -rw-r--r-- | mumd/src/state.rs | 425 | ||||
| -rw-r--r-- | mumlib/src/command.rs | 10 | ||||
| -rw-r--r-- | mumlib/src/error.rs | 6 | ||||
| -rw-r--r-- | mumlib/src/lib.rs | 2 | ||||
| -rw-r--r-- | mumlib/src/state.rs | 290 |
8 files changed, 639 insertions, 283 deletions
diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 82fcab6..2473195 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -3,7 +3,10 @@ use colored::Colorize; use ipc_channel::ipc::{self, IpcSender}; use mumlib::command::{Command, CommandResponse}; use mumlib::setup_logger; -use std::{fs, io}; +use mumlib::state::Channel; +use std::{fs, io, iter}; + +const INDENTATION: &str = " "; macro_rules! err_print { ($func:expr) => { @@ -18,26 +21,28 @@ fn main() { let mut app = App::new("mumctl") .setting(AppSettings::ArgRequiredElseHelp) - .subcommand(SubCommand::with_name("server") - .setting(AppSettings::ArgRequiredElseHelp) - .subcommand(SubCommand::with_name("connect") - .setting(AppSettings::ArgRequiredElseHelp) - .arg(Arg::with_name("host") - .required(true) - .index(1)) - .arg(Arg::with_name("username") - .required(true) - .index(2))) - .subcommand(SubCommand::with_name("disconnect"))) - .subcommand(SubCommand::with_name("channel") - .setting(AppSettings::ArgRequiredElseHelp) - .subcommand(SubCommand::with_name("list") - .arg(Arg::with_name("short") - .short("s") - .long("short"))) - .subcommand(SubCommand::with_name("connect") - .arg(Arg::with_name("channel") - .required(true)))) + .subcommand( + SubCommand::with_name("server") + .setting(AppSettings::ArgRequiredElseHelp) + .subcommand( + SubCommand::with_name("connect") + .setting(AppSettings::ArgRequiredElseHelp) + .arg(Arg::with_name("host").required(true).index(1)) + .arg(Arg::with_name("username").required(true).index(2)), + ) + .subcommand(SubCommand::with_name("disconnect")), + ) + .subcommand( + SubCommand::with_name("channel") + .setting(AppSettings::ArgRequiredElseHelp) + .subcommand( + SubCommand::with_name("list") + .arg(Arg::with_name("short").short("s").long("short")), + ) + .subcommand( + SubCommand::with_name("connect").arg(Arg::with_name("channel").required(true)), + ), + ) .subcommand(SubCommand::with_name("status")) .subcommand(SubCommand::with_name("config") .arg(Arg::with_name("name") @@ -53,7 +58,7 @@ fn main() { .long("fish"))); let matches = app.clone().get_matches(); - + if let Some(matches) = matches.subcommand_matches("server") { if let Some(matches) = matches.subcommand_matches("connect") { let host = matches.value_of("host").unwrap(); @@ -70,9 +75,12 @@ fn main() { } else if let Some(matches) = matches.subcommand_matches("channel") { if let Some(_matches) = matches.subcommand_matches("list") { match send_command(Command::ChannelList) { - Ok(res) => { - println!("{:#?}", res.unwrap()); - } + Ok(res) => match res { + Some(CommandResponse::ChannelList { channels }) => { + print_channel(&channels, 0); + } + _ => unreachable!(), + }, Err(e) => println!("{} {}", "error:".red(), e), } } else if let Some(matches) = matches.subcommand_matches("connect") { @@ -82,13 +90,36 @@ fn main() { } } else if let Some(_matches) = matches.subcommand_matches("status") { match send_command(Command::Status) { - Ok(res) => { - println!("{:#?}", res.unwrap()); - } + Ok(res) => match res { + Some(CommandResponse::Status { server_state }) => { + println!( + "Connected to {} as {}", + server_state.host, server_state.username + ); + let own_channel = server_state + .channels + .iter() + .find(|e| e.users.iter().any(|e| e.name == server_state.username)) + .unwrap(); + println!( + "Currently in {} with {} other client{}:", + own_channel.name, + own_channel.users.len() - 1, + if own_channel.users.len() == 2 { + "" + } else { + "s" + } + ); + println!("{}{}", INDENTATION, own_channel.name); + for user in &own_channel.users { + println!("{}{}{}", INDENTATION, INDENTATION, user); + } + } + _ => unreachable!(), + }, Err(e) => println!("{} {}", "error:".red(), e), } - let res = send_command(Command::Status).unwrap().unwrap(); - println!("{:#?}", res); } else if let Some(matches) = matches.subcommand_matches("config") { let name = matches.value_of("name").unwrap(); let value = matches.value_of("value").unwrap(); @@ -103,25 +134,22 @@ fn main() { } } } else if let Some(matches) = matches.subcommand_matches("completions") { - app.gen_completions_to("mumctl", - match matches.value_of("shell").unwrap_or("zsh") { - "bash" => { - Shell::Bash - }, - "fish" => { - Shell::Fish - }, - _ => { - Shell::Zsh - }, - }, - &mut io::stdout()); - return; + app.gen_completions_to( + "mumctl", + match matches.value_of("shell").unwrap_or("zsh") { + "bash" => Shell::Bash, + "fish" => Shell::Fish, + _ => Shell::Zsh, + }, + &mut io::stdout(), + ); + return; }; } fn send_command(command: Command) -> mumlib::error::Result<Option<CommandResponse>> { - let (tx_client, rx_client) = ipc::channel::<mumlib::error::Result<Option<CommandResponse>>>().unwrap(); + let (tx_client, rx_client) = + ipc::channel::<mumlib::error::Result<Option<CommandResponse>>>().unwrap(); let server_name = fs::read_to_string("/var/tmp/mumd-oneshot").unwrap(); //TODO don't panic @@ -131,3 +159,28 @@ fn send_command(command: Command) -> mumlib::error::Result<Option<CommandRespons rx_client.recv().unwrap() } + +fn print_channel(channel: &Channel, depth: usize) { + println!( + "{}{}{}", + iter::repeat(INDENTATION).take(depth).collect::<String>(), + channel.name.bold(), + if channel.max_users != 0 { + format!(" {}/{}", channel.users.len(), channel.max_users) + } else { + "".to_string() + } + ); + for user in &channel.users { + println!( + "{}-{}", + iter::repeat(INDENTATION) + .take(depth + 1) + .collect::<String>(), + user + ); + } + for child in &channel.children { + print_channel(child, depth + 1); + } +} diff --git a/mumd/src/command.rs b/mumd/src/command.rs index 57eaaa3..a035a26 100644 --- a/mumd/src/command.rs +++ b/mumd/src/command.rs @@ -8,7 +8,10 @@ use tokio::sync::mpsc; pub async fn handle( state: Arc<Mutex<State>>, - mut command_receiver: mpsc::UnboundedReceiver<(Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)>, + mut command_receiver: mpsc::UnboundedReceiver<( + Command, + IpcSender<mumlib::error::Result<Option<CommandResponse>>>, + )>, ) { debug!("Begin listening for commands"); while let Some(command) = command_receiver.recv().await { diff --git a/mumd/src/main.rs b/mumd/src/main.rs index 3a0d7ec..5ae077a 100644 --- a/mumd/src/main.rs +++ b/mumd/src/main.rs @@ -7,7 +7,7 @@ use crate::network::ConnectionInfo; use crate::state::State; use futures::join; -use ipc_channel::ipc::{IpcSender, IpcOneShotServer}; +use ipc_channel::ipc::{IpcOneShotServer, IpcSender}; use log::*; use mumble_protocol::control::ControlPacket; use mumble_protocol::crypt::ClientCryptState; @@ -27,14 +27,14 @@ async fn main() { // For simplicity we don't deal with re-syncing, real applications would have to. let (crypt_state_sender, crypt_state_receiver) = mpsc::channel::<ClientCryptState>(1); // crypt state should always be consumed before sending a new one let (packet_sender, packet_receiver) = mpsc::unbounded_channel::<ControlPacket<Serverbound>>(); - let (command_sender, command_receiver) = mpsc::unbounded_channel::<(Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)>(); + let (command_sender, command_receiver) = mpsc::unbounded_channel::<( + Command, + IpcSender<mumlib::error::Result<Option<CommandResponse>>>, + )>(); let (connection_info_sender, connection_info_receiver) = watch::channel::<Option<ConnectionInfo>>(None); - let state = State::new( - packet_sender, - connection_info_sender, - ); + let state = State::new(packet_sender, connection_info_sender); let state = Arc::new(Mutex::new(state)); let (_, _, _, e) = join!( @@ -49,11 +49,9 @@ async fn main() { connection_info_receiver.clone(), crypt_state_receiver, ), - command::handle( - state, - command_receiver, - ), - spawn_blocking(move || { // IpcSender is blocking + command::handle(state, command_receiver,), + spawn_blocking(move || { + // IpcSender is blocking receive_oneshot_commands(command_sender); }), ); @@ -61,16 +59,31 @@ async fn main() { } fn receive_oneshot_commands( - command_sender: mpsc::UnboundedSender<(Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)>, + command_sender: mpsc::UnboundedSender<( + Command, + IpcSender<mumlib::error::Result<Option<CommandResponse>>>, + )>, ) { loop { // create listener - let (server, server_name): (IpcOneShotServer<(Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)>, String) = IpcOneShotServer::new().unwrap(); + let (server, server_name): ( + IpcOneShotServer<( + Command, + IpcSender<mumlib::error::Result<Option<CommandResponse>>>, + )>, + String, + ) = IpcOneShotServer::new().unwrap(); fs::write("/var/tmp/mumd-oneshot", &server_name).unwrap(); debug!("Listening to {}", server_name); // receive command and response channel - let (_, conn): (_, (Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)) = server.accept().unwrap(); + let (_, conn): ( + _, + ( + Command, + IpcSender<mumlib::error::Result<Option<CommandResponse>>>, + ), + ) = server.accept().unwrap(); debug!("Sending command {:?} to command handler", conn.0); command_sender.send(conn).unwrap(); } diff --git a/mumd/src/state.rs b/mumd/src/state.rs index fd1c831..55fd8ae 100644 --- a/mumd/src/state.rs +++ b/mumd/src/state.rs @@ -6,10 +6,12 @@ use mumble_protocol::control::msgs; use mumble_protocol::control::ControlPacket; use mumble_protocol::voice::Serverbound; use mumlib::command::{Command, CommandResponse}; -use mumlib::state::Server; +use mumlib::error::{ChannelIdentifierError, Error}; +use serde::{Deserialize, Serialize}; +use std::collections::hash_map::Entry; +use std::collections::HashMap; use std::net::ToSocketAddrs; use tokio::sync::{mpsc, watch}; -use mumlib::error::{ChannelIdentifierError, Error}; #[derive(Clone, Debug, Eq, PartialEq)] pub enum StatePhase { @@ -26,9 +28,6 @@ pub struct State { connection_info_sender: watch::Sender<Option<ConnectionInfo>>, phase_watcher: (watch::Sender<StatePhase>, watch::Receiver<StatePhase>), - - username: Option<String>, - session_id: Option<u32>, } impl State { @@ -42,8 +41,6 @@ impl State { packet_sender, connection_info_sender, phase_watcher: watch::channel(StatePhase::Disconnected), - username: None, - session_id: None, } } @@ -83,7 +80,7 @@ impl State { }; let mut msg = msgs::UserState::new(); - msg.set_session(self.session_id.unwrap()); + msg.set_session(self.server.as_ref().unwrap().session_id.unwrap()); msg.set_channel_id(id); self.packet_sender.send(msg.into()).unwrap(); (false, Ok(None)) @@ -92,8 +89,13 @@ impl State { if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected) { return (false, Err(Error::DisconnectedError)); } - (false, Ok(Some(CommandResponse::ChannelList { - channels: self.server().unwrap().channels().clone(), + ( + false, + Ok(Some(CommandResponse::ChannelList { + channels: into_channel( + self.server.as_ref().unwrap().channels(), + self.server.as_ref().unwrap().users(), + ), })), ) } @@ -106,14 +108,19 @@ impl State { if !matches!(*self.phase_receiver().borrow(), StatePhase::Disconnected) { return (false, Err(Error::AlreadyConnectedError)); } - self.server = Some(Server::new()); - self.username = Some(username); + let mut server = Server::new(); + server.username = Some(username); + server.host = Some(format!("{}:{}", host, port)); + self.server = Some(server); self.phase_watcher .0 .broadcast(StatePhase::Connecting) .unwrap(); - let socket_addr = match (host.as_ref(), port).to_socket_addrs().map(|mut e| e.next()) { + let socket_addr = match (host.as_ref(), port) + .to_socket_addrs() + .map(|mut e| e.next()) + { Ok(Some(v)) => v, _ => { warn!("Error parsing server addr"); @@ -136,8 +143,7 @@ impl State { ( false, Ok(Some(CommandResponse::Status { - username: self.username.clone(), - server_state: self.server.clone().unwrap(), //guaranteed not to panic because if we are connected, server is guaranteed to be Some + server_state: self.server.as_ref().unwrap().into(), //guaranteed not to panic because if we are connected, server is guaranteed to be Some })), ) } @@ -146,8 +152,6 @@ impl State { return (false, Err(Error::DisconnectedError)); } - self.session_id = None; - self.username = None; self.server = None; self.audio.clear_clients(); @@ -171,11 +175,11 @@ impl State { } if !msg.has_name() { warn!("Missing name in initial user state"); - } else if msg.get_name() == self.username.as_ref().unwrap() { - match self.session_id { + } else if msg.get_name() == self.server.as_ref().unwrap().username.as_ref().unwrap() { + match self.server.as_ref().unwrap().session_id { None => { debug!("Found our session id: {}", msg.get_session()); - self.session_id = Some(msg.get_session()); + self.server_mut().unwrap().session_id = Some(msg.get_session()); } Some(session) => { if session != msg.get_session() { @@ -218,7 +222,384 @@ impl State { pub fn server_mut(&mut self) -> Option<&mut Server> { self.server.as_mut() } - pub fn username(&self) -> Option<&String> { - self.username.as_ref() + pub fn username(&self) -> Option<&str> { + self.server.as_ref().map(|e| e.username()).flatten() + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Server { + channels: HashMap<u32, Channel>, + users: HashMap<u32, User>, + pub welcome_text: Option<String>, + + username: Option<String>, + session_id: Option<u32>, + + host: Option<String>, +} + +impl Server { + pub fn new() -> Self { + Self { + channels: HashMap::new(), + users: HashMap::new(), + welcome_text: None, + username: None, + session_id: None, + host: None, + } + } + + pub fn parse_server_sync(&mut self, mut msg: msgs::ServerSync) { + if msg.has_welcome_text() { + self.welcome_text = Some(msg.take_welcome_text()); + } + } + + pub fn parse_channel_state(&mut self, msg: msgs::ChannelState) { + if !msg.has_channel_id() { + warn!("Can't parse channel state without channel id"); + return; + } + match self.channels.entry(msg.get_channel_id()) { + Entry::Vacant(e) => { + e.insert(Channel::new(msg)); + } + Entry::Occupied(mut e) => e.get_mut().parse_channel_state(msg), + } + } + + pub fn parse_channel_remove(&mut self, msg: msgs::ChannelRemove) { + if !msg.has_channel_id() { + warn!("Can't parse channel remove without channel id"); + return; + } + match self.channels.entry(msg.get_channel_id()) { + Entry::Vacant(_) => { + warn!("Attempted to remove channel that doesn't exist"); + } + Entry::Occupied(e) => { + e.remove(); + } + } + } + + pub fn parse_user_state(&mut self, msg: msgs::UserState) { + if !msg.has_session() { + warn!("Can't parse user state without session"); + return; + } + match self.users.entry(msg.get_session()) { + Entry::Vacant(e) => { + e.insert(User::new(msg)); + } + Entry::Occupied(mut e) => e.get_mut().parse_user_state(msg), + } + } + + pub fn channels(&self) -> &HashMap<u32, Channel> { + &self.channels + } + + pub fn users(&self) -> &HashMap<u32, User> { + &self.users + } + + pub fn username(&self) -> Option<&str> { + self.username.as_ref().map(|e| e.as_str()) + } +} + +impl From<&Server> for mumlib::state::Server { + fn from(server: &Server) -> Self { + mumlib::state::Server { + channels: into_channel(server.channels(), server.users()), + welcome_text: server.welcome_text.clone(), + username: server.username.clone().unwrap(), + host: server.host.as_ref().unwrap().clone(), + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Channel { + description: Option<String>, + links: Vec<u32>, + max_users: u32, + name: String, + parent: Option<u32>, + position: i32, +} + +impl Channel { + pub fn new(mut msg: msgs::ChannelState) -> Self { + Self { + description: if msg.has_description() { + Some(msg.take_description()) + } else { + None + }, + links: Vec::new(), + max_users: msg.get_max_users(), + name: msg.take_name(), + parent: if msg.has_parent() { + Some(msg.get_parent()) + } else { + None + }, + position: msg.get_position(), + } + } + + pub fn parse_channel_state(&mut self, mut msg: msgs::ChannelState) { + if msg.has_description() { + self.description = Some(msg.take_description()); + } + self.links = msg.take_links(); + if msg.has_max_users() { + self.max_users = msg.get_max_users(); + } + if msg.has_name() { + self.name = msg.take_name(); + } + if msg.has_parent() { + self.parent = Some(msg.get_parent()); + } + if msg.has_position() { + self.position = msg.get_position(); + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn path(&self, channels: &HashMap<u32, Channel>) -> String { + match &self.parent { + Some(t) => format!("{}/{}", channels.get(t).unwrap().path(channels), self.name), + None => self.name.clone(), + } + } +} + +#[derive(Debug)] +struct ProtoTree<'a> { + channel: Option<&'a Channel>, + children: HashMap<u32, ProtoTree<'a>>, + users: Vec<&'a User>, +} + +impl<'a> ProtoTree<'a> { + fn walk_and_add( + &mut self, + channel: &'a Channel, + users: &HashMap<u32, Vec<&'a User>>, + walk: &[u32], + ) { + match walk { + [] => unreachable!("Walks should always have at least one element"), + &[node] => { + let pt = self.children.entry(node).or_insert(ProtoTree { + channel: None, + children: HashMap::new(), + users: Vec::new(), + }); + pt.channel = Some(channel); + pt.users = users.get(&node).map(|e| e.clone()).unwrap_or(Vec::new()); + } + longer => { + self.children + .entry(longer[0]) + .or_insert(ProtoTree { + channel: None, + children: HashMap::new(), + users: Vec::new(), + }) + .walk_and_add(channel, users, &walk[1..]); + } + } + } +} + +impl<'a> From<&ProtoTree<'a>> for mumlib::state::Channel { + fn from(tree: &ProtoTree<'a>) -> Self { + let mut channel = mumlib::state::Channel::from(tree.channel.unwrap()); + let mut children = tree + .children + .iter() + .map(|e| { + ( + e.1.channel.unwrap().position, + mumlib::state::Channel::from(e.1), + ) + }) + .collect::<Vec<_>>(); + children.sort_by_key(|e| (e.0, e.1.name.clone())); + channel.children = children.into_iter().map(|e| e.1).collect(); + channel.users = tree.users.iter().map(|e| (*e).into()).collect(); + channel + } +} + +pub fn into_channel( + channels: &HashMap<u32, Channel>, + users: &HashMap<u32, User>, +) -> mumlib::state::Channel { + let mut walks = Vec::new(); + + let mut channel_lookup = HashMap::new(); + + for user in users.values() { + channel_lookup + .entry(user.channel) + .or_insert(Vec::new()) + .push(user); + } + + for (channel_id, channel) in channels { + let mut walk = Vec::new(); + let mut current = *channel_id; + while let Some(next) = channels.get(¤t).unwrap().parent { + walk.push(current); + current = next; + } + walk.reverse(); + + if walk.len() > 0 { + walks.push((walk, channel)); + } + } + + //root node is ignored because of how walk_and_add is implemented on ProtoTree + let mut proto_tree = ProtoTree { + channel: Some(channels.get(&0).unwrap()), + children: HashMap::new(), + users: channel_lookup + .get(&0) + .map(|e| e.clone()) + .unwrap_or(Vec::new()), + }; + + for (walk, channel) in walks { + proto_tree.walk_and_add(channel, &channel_lookup, &walk); + } + + (&proto_tree).into() +} + +impl From<&Channel> for mumlib::state::Channel { + fn from(channel: &Channel) -> Self { + mumlib::state::Channel { + description: channel.description.clone(), + links: Vec::new(), + max_users: channel.max_users, + name: channel.name.clone(), + children: Vec::new(), + users: Vec::new(), + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct User { + channel: u32, + comment: Option<String>, + hash: Option<String>, + name: String, + priority_speaker: bool, + recording: bool, + + suppress: bool, // by me + self_mute: bool, // by self + self_deaf: bool, // by self + mute: bool, // by admin + deaf: bool, // by admin +} + +impl User { + pub fn new(mut msg: msgs::UserState) -> Self { + Self { + channel: msg.get_channel_id(), + comment: if msg.has_comment() { + Some(msg.take_comment()) + } else { + None + }, + hash: if msg.has_hash() { + Some(msg.take_hash()) + } else { + None + }, + name: msg.take_name(), + priority_speaker: msg.has_priority_speaker() && msg.get_priority_speaker(), + recording: msg.has_recording() && msg.get_recording(), + suppress: msg.has_suppress() && msg.get_suppress(), + self_mute: msg.has_self_mute() && msg.get_self_mute(), + self_deaf: msg.has_self_deaf() && msg.get_self_deaf(), + mute: msg.has_mute() && msg.get_mute(), + deaf: msg.has_deaf() && msg.get_deaf(), + } + } + + pub fn parse_user_state(&mut self, mut msg: msgs::UserState) { + if msg.has_channel_id() { + self.channel = msg.get_channel_id(); + } + if msg.has_comment() { + self.comment = Some(msg.take_comment()); + } + if msg.has_hash() { + self.hash = Some(msg.take_hash()); + } + if msg.has_name() { + self.name = msg.take_name(); + } + if msg.has_priority_speaker() { + self.priority_speaker = msg.get_priority_speaker(); + } + if msg.has_recording() { + self.recording = msg.get_recording(); + } + if msg.has_suppress() { + self.suppress = msg.get_suppress(); + } + if msg.has_self_mute() { + self.self_mute = msg.get_self_mute(); + } + if msg.has_self_deaf() { + self.self_deaf = msg.get_self_deaf(); + } + if msg.has_mute() { + self.mute = msg.get_mute(); + } + if msg.has_deaf() { + self.deaf = msg.get_deaf(); + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn channel(&self) -> u32 { + self.channel + } +} + +impl From<&User> for mumlib::state::User { + fn from(user: &User) -> Self { + mumlib::state::User { + comment: user.comment.clone(), + hash: user.hash.clone(), + name: user.name.clone(), + priority_speaker: user.priority_speaker, + recording: user.recording, + suppress: user.suppress, + self_mute: user.self_mute, + self_deaf: user.self_deaf, + mute: user.mute, + deaf: user.deaf, + } } } diff --git a/mumlib/src/command.rs b/mumlib/src/command.rs index b27d3ca..b4ab07a 100644 --- a/mumlib/src/command.rs +++ b/mumlib/src/command.rs @@ -1,7 +1,6 @@ use crate::state::{Channel, Server}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; #[derive(Clone, Debug, Deserialize, Serialize)] pub enum Command { @@ -22,11 +21,6 @@ pub enum Command { #[derive(Debug, Deserialize, Serialize)] pub enum CommandResponse { - ChannelList { - channels: HashMap<u32, Channel>, - }, - Status { - username: Option<String>, - server_state: Server, - }, + ChannelList { channels: Channel }, + Status { server_state: Server }, } diff --git a/mumlib/src/error.rs b/mumlib/src/error.rs index 6c66c1f..a4c6dcb 100644 --- a/mumlib/src/error.rs +++ b/mumlib/src/error.rs @@ -1,6 +1,6 @@ -use serde::{Serialize, Deserialize}; -use std::fmt::Display; use serde::export::Formatter; +use serde::{Deserialize, Serialize}; +use std::fmt::Display; pub type Result<T> = std::result::Result<T, Error>; @@ -37,4 +37,4 @@ impl Display for ChannelIdentifierError { ChannelIdentifierError::Ambiguous => write!(f, "Ambiguous channel identifier"), } } -}
\ No newline at end of file +} diff --git a/mumlib/src/lib.rs b/mumlib/src/lib.rs index b77653c..d989a7e 100644 --- a/mumlib/src/lib.rs +++ b/mumlib/src/lib.rs @@ -1,6 +1,6 @@ pub mod command; -pub mod state; pub mod error; +pub mod state; use colored::*; use log::*; diff --git a/mumlib/src/state.rs b/mumlib/src/state.rs index 51fb492..b09726e 100644 --- a/mumlib/src/state.rs +++ b/mumlib/src/state.rs @@ -1,224 +1,136 @@ -use log::*; -use mumble_protocol::control::msgs; +use serde::export::Formatter; use serde::{Deserialize, Serialize}; -use std::collections::hash_map::Entry; -use std::collections::HashMap; +use std::fmt::Display; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Server { - channels: HashMap<u32, Channel>, - users: HashMap<u32, User>, + pub channels: Channel, pub welcome_text: Option<String>, -} - -impl Server { - pub fn new() -> Self { - Self { - channels: HashMap::new(), - users: HashMap::new(), - welcome_text: None, - } - } - - pub fn parse_server_sync(&mut self, mut msg: msgs::ServerSync) { - if msg.has_welcome_text() { - self.welcome_text = Some(msg.take_welcome_text()); - } - } - - pub fn parse_channel_state(&mut self, msg: msgs::ChannelState) { - if !msg.has_channel_id() { - warn!("Can't parse channel state without channel id"); - return; - } - match self.channels.entry(msg.get_channel_id()) { - Entry::Vacant(e) => { - e.insert(Channel::new(msg)); - } - Entry::Occupied(mut e) => e.get_mut().parse_channel_state(msg), - } - } - - pub fn parse_channel_remove(&mut self, msg: msgs::ChannelRemove) { - if !msg.has_channel_id() { - warn!("Can't parse channel remove without channel id"); - return; - } - match self.channels.entry(msg.get_channel_id()) { - Entry::Vacant(_) => { - warn!("Attempted to remove channel that doesn't exist"); - } - Entry::Occupied(e) => { - e.remove(); - } - } - } - - pub fn parse_user_state(&mut self, msg: msgs::UserState) { - if !msg.has_session() { - warn!("Can't parse user state without session"); - return; - } - match self.users.entry(msg.get_session()) { - Entry::Vacant(e) => { - e.insert(User::new(msg)); - } - Entry::Occupied(mut e) => e.get_mut().parse_user_state(msg), - } - } - - pub fn channels(&self) -> &HashMap<u32, Channel> { - &self.channels - } - - pub fn users(&self) -> &HashMap<u32, User> { - &self.users - } + pub username: String, + pub host: String, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Channel { - description: Option<String>, - links: Vec<u32>, - max_users: u32, - name: String, - parent: Option<u32>, - position: i32, + pub description: Option<String>, + pub links: Vec<Vec<usize>>, //to represent several walks through the tree to find channels its linked to + pub max_users: u32, + pub name: String, + pub children: Vec<Channel>, + pub users: Vec<User>, } impl Channel { - pub fn new(mut msg: msgs::ChannelState) -> Self { - Self { - description: if msg.has_description() { - Some(msg.take_description()) + pub fn iter(&self) -> Iter<'_> { + Iter { + me: Some(&self), + channel: if self.children.len() > 0 { + Some(0) } else { None }, - links: Vec::new(), - max_users: msg.get_max_users(), - name: msg.take_name(), - parent: if msg.has_parent() { - Some(msg.get_parent()) - } else { - None - }, - position: msg.get_position(), + channels: self.children.iter().map(|e| e.iter()).collect(), } } - pub fn parse_channel_state(&mut self, mut msg: msgs::ChannelState) { - if msg.has_description() { - self.description = Some(msg.take_description()); - } - self.links = msg.take_links(); - if msg.has_max_users() { - self.max_users = msg.get_max_users(); - } - if msg.has_name() { - self.name = msg.take_name(); - } - if msg.has_parent() { - self.parent = Some(msg.get_parent()); - } - if msg.has_position() { - self.position = msg.get_position(); + pub fn users_iter(&self) -> UsersIter<'_> { + UsersIter { + channels: self.children.iter().map(|e| e.users_iter()).collect(), + channel: if self.children.len() > 0 { + Some(0) + } else { + None + }, + user: if self.users.len() > 0 { Some(0) } else { None }, + users: &self.users, } } +} - pub fn name(&self) -> &str { - &self.name - } +pub struct Iter<'a> { + me: Option<&'a Channel>, + channel: Option<usize>, + channels: Vec<Iter<'a>>, +} - pub fn path(&self, channels: &HashMap<u32, Channel>) -> String { - match &self.parent { - Some(t) => format!("{}/{}", channels.get(t).unwrap().path(channels), self.name), - None => self.name.clone(), +impl<'a> Iterator for Iter<'a> { + type Item = &'a Channel; + + fn next(&mut self) -> Option<Self::Item> { + if self.me.is_some() { + self.me.take() + } else if let Some(mut c) = self.channel { + let mut n = self.channels[c].next(); + while n.is_none() { + c += 1; + if c >= self.channels.len() { + self.channel = None; + return None; + } + n = self.channels[c].next(); + } + self.channel = Some(c); + n + } else { + None } } } -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct User { - channel: u32, - comment: Option<String>, - hash: Option<String>, - name: String, - priority_speaker: bool, - recording: bool, - - suppress: bool, // by me - self_mute: bool, // by self - self_deaf: bool, // by self - mute: bool, // by admin - deaf: bool, // by admin +pub struct UsersIter<'a> { + channel: Option<usize>, + channels: Vec<UsersIter<'a>>, + user: Option<usize>, + users: &'a [User], } -impl User { - pub fn new(mut msg: msgs::UserState) -> Self { - Self { - channel: msg.get_channel_id(), - comment: if msg.has_comment() { - Some(msg.take_comment()) - } else { - None - }, - hash: if msg.has_hash() { - Some(msg.take_hash()) - } else { - None - }, - name: msg.take_name(), - priority_speaker: msg.has_priority_speaker() && msg.get_priority_speaker(), - recording: msg.has_recording() && msg.get_recording(), - suppress: msg.has_suppress() && msg.get_suppress(), - self_mute: msg.has_self_mute() && msg.get_self_mute(), - self_deaf: msg.has_self_deaf() && msg.get_self_deaf(), - mute: msg.has_mute() && msg.get_mute(), - deaf: msg.has_deaf() && msg.get_deaf(), - } - } +impl<'a> Iterator for UsersIter<'a> { + type Item = &'a User; - pub fn parse_user_state(&mut self, mut msg: msgs::UserState) { - if msg.has_channel_id() { - self.channel = msg.get_channel_id(); - } - if msg.has_comment() { - self.comment = Some(msg.take_comment()); - } - if msg.has_hash() { - self.hash = Some(msg.take_hash()); - } - if msg.has_name() { - self.name = msg.take_name(); - } - if msg.has_priority_speaker() { - self.priority_speaker = msg.get_priority_speaker(); - } - if msg.has_recording() { - self.recording = msg.get_recording(); - } - if msg.has_suppress() { - self.suppress = msg.get_suppress(); - } - if msg.has_self_mute() { - self.self_mute = msg.get_self_mute(); - } - if msg.has_self_deaf() { - self.self_deaf = msg.get_self_deaf(); - } - if msg.has_mute() { - self.mute = msg.get_mute(); - } - if msg.has_deaf() { - self.deaf = msg.get_deaf(); + fn next(&mut self) -> Option<Self::Item> { + if let Some(u) = self.user { + let ret = Some(&self.users[u]); + if u + 1 < self.users.len() { + self.user = Some(u + 1); + } else { + self.user = None; + } + ret + } else if let Some(mut c) = self.channel { + let mut n = self.channels[c].next(); + while n.is_none() { + c += 1; + if c >= self.channels.len() { + self.channel = None; + return None; + } + n = self.channels[c].next(); + } + self.channel = Some(c); + n + } else { + None } } +} - pub fn name(&self) -> &str { - &self.name - } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct User { + pub comment: Option<String>, + pub hash: Option<String>, + pub name: String, + pub priority_speaker: bool, + pub recording: bool, + + pub suppress: bool, // by me + pub self_mute: bool, // by self + pub self_deaf: bool, // by self + pub mute: bool, // by admin + pub deaf: bool, // by admin +} - pub fn channel(&self) -> u32 { - self.channel +impl Display for User { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) } } |
