From de856d5e43ecadcd876bdf03800ecc5421347872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Thu, 15 Oct 2020 20:52:27 +0200 Subject: initial cli --- Cargo.toml | 1 + mumctl/Cargo.toml | 7 +- mumctl/src/main.rs | 32 +++++++- mumd/Cargo.toml | 7 +- mumd/src/command.rs | 53 ++++--------- mumd/src/main.rs | 117 +++++++-------------------- mumd/src/state.rs | 223 +--------------------------------------------------- 7 files changed, 88 insertions(+), 352 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index db28621..be2b0e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ members = [ "mumd", "mumctl", + "mumlib" ] diff --git a/mumctl/Cargo.toml b/mumctl/Cargo.toml index fca5037..3a432d9 100644 --- a/mumctl/Cargo.toml +++ b/mumctl/Cargo.toml @@ -9,7 +9,10 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +mumlib = { path = "../mumlib" } + +log = "0.4" +ipc-channel = "0.14" + #clap = "2.33" #cursive = "0.15" -#ipc-channel = "0.14" -#serde = { version = "1.0", features = ["derive"] } diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index e7a11a9..403447f 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -1,3 +1,33 @@ +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use log::*; +use mumlib::command::{Command, CommandResponse}; +use mumlib::setup_logger; +use std::fs; + fn main() { - println!("Hello, world!"); + setup_logger(); + + // MUMCTL + //temp send command and channel to listener + debug!("Creating channel"); + let (tx_client, rx_client): (IpcSender, ()>>, + IpcReceiver, ()>>) = ipc::channel().unwrap(); + + let server_name = fs::read_to_string("/var/tmp/mumd-oneshot").unwrap(); //TODO don't panic + debug!("Connecting to mumd at {}", server_name); + let tx0 = IpcSender::connect(server_name).unwrap(); + let connect_command = Command::ServerConnect { + host: "10.0.0.10".to_string(), + port: 64738u16, + username: "gustav-mumd".to_string(), + accept_invalid_cert: true, + }; + debug!("Sending {:?} to mumd", connect_command); + tx0.send(( + connect_command, + tx_client)) .unwrap(); + + debug!("Reading response"); + let response = rx_client.recv().unwrap(); + debug!("{:?}", response); } diff --git a/mumd/Cargo.toml b/mumd/Cargo.toml index 72f9167..9101b43 100644 --- a/mumd/Cargo.toml +++ b/mumd/Cargo.toml @@ -9,18 +9,20 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +mumlib = { path = "../mumlib" } + argparse = "0.2" bytes = "0.5" -colored = "2.0" cpal = { git = "https://github.com/RustAudio/cpal" } -fern = "0.5" futures = "0.3" futures-util = "0.3" +ipc-channel = "0.14" log = "0.4" mumble-protocol = "0.3" native-tls = "0.2" openssl = { version = "0.10", optional = true } opus = "0.2" +serde = { version = "1.0", features = ["derive"] } tokio = { version = "0.2", features = ["full"] } tokio-tls = "0.3" tokio-util = { version = "0.3", features = ["codec", "udp"] } @@ -28,4 +30,3 @@ tokio-util = { version = "0.3", features = ["codec", "udp"] } #clap = "2.33" #compressor = "0.3" #daemonize = "0.4" -#ipc-channel = "0.14" diff --git a/mumd/src/command.rs b/mumd/src/command.rs index b4bd1b7..9adf7d8 100644 --- a/mumd/src/command.rs +++ b/mumd/src/command.rs @@ -1,54 +1,33 @@ -use crate::state::{Channel, Server, State, StatePhase}; +use crate::state::{State, StatePhase}; +use ipc_channel::ipc::IpcSender; use log::*; -use std::collections::HashMap; +use mumlib::command::{Command, CommandResponse}; use std::sync::{Arc, Mutex}; use tokio::sync::mpsc; -#[derive(Clone, Debug)] -pub enum Command { - ChannelJoin { - channel_id: u32, - }, - ChannelList, - ServerConnect { - host: String, - port: u16, - username: String, - accept_invalid_cert: bool, //TODO ask when connecting - }, - ServerDisconnect, - Status, -} - -#[derive(Debug)] -pub enum CommandResponse { - ChannelList { - channels: HashMap, - }, - Status { - username: Option, - server_state: Server, - }, -} - pub async fn handle( state: Arc>, - mut command_receiver: mpsc::UnboundedReceiver, - command_response_sender: mpsc::UnboundedSender, ()>>, + mut command_receiver: mpsc::UnboundedReceiver<(Command, IpcSender, ()>>)>, ) { - //TODO err if not connected - while let Some(command) = command_receiver.recv().await { - debug!("Parsing command {:?}", command); + debug!("Begin listening for commands"); + loop { + debug!("Enter loop"); + let command = command_receiver.recv().await.unwrap(); + debug!("Received command {:?}", command.0); let mut state = state.lock().unwrap(); - let (wait_for_connected, command_response) = state.handle_command(command).await; + let (wait_for_connected, command_response) = state.handle_command(command.0).await; if wait_for_connected { let mut watcher = state.phase_receiver(); drop(state); while !matches!(watcher.recv().await.unwrap(), StatePhase::Connected) {} } - command_response_sender.send(command_response).unwrap(); + command.1.send(command_response).unwrap(); } + //TODO err if not connected + //while let Some(command) = command_receiver.recv().await { + // debug!("Parsing command {:?}", command); + //} - debug!("Finished handling commands"); + //debug!("Finished handling commands"); } diff --git a/mumd/src/main.rs b/mumd/src/main.rs index f837a52..8639c35 100644 --- a/mumd/src/main.rs +++ b/mumd/src/main.rs @@ -3,96 +3,42 @@ mod command; mod network; mod state; -use crate::command::{Command, CommandResponse}; use crate::network::ConnectionInfo; use crate::state::State; -use argparse::ArgumentParser; -use argparse::Store; -use argparse::StoreTrue; -use colored::*; use futures::join; +use ipc_channel::ipc::{IpcSender, IpcOneShotServer}; use log::*; use mumble_protocol::control::ControlPacket; use mumble_protocol::crypt::ClientCryptState; use mumble_protocol::voice::Serverbound; +use mumlib::command::{Command, CommandResponse}; +use mumlib::setup_logger; +use std::fs; use std::sync::{Arc, Mutex}; -use std::time::Duration; use tokio::sync::{mpsc, watch}; +use tokio::task::spawn_blocking; #[tokio::main] async fn main() { - // setup logger - fern::Dispatch::new() - .format(|out, message, record| { - let message = message.to_string(); - out.finish(format_args!( - "{} {}:{}{}{}", - //TODO runtime flag that disables color - match record.level() { - Level::Error => "ERROR".red(), - Level::Warn => "WARN ".yellow(), - Level::Info => "INFO ".normal(), - Level::Debug => "DEBUG".green(), - Level::Trace => "TRACE".normal(), - }, - record.file().unwrap(), - record.line().unwrap(), - if message.chars().any(|e| e == '\n') { - "\n" - } else { - " " - }, - message - )) - }) - .level(log::LevelFilter::Debug) - .chain(std::io::stderr()) - .apply() - .unwrap(); - - // Handle command line arguments - let mut server_host = "".to_string(); - let mut server_port = 64738u16; - let mut username = "EchoBot".to_string(); - let mut accept_invalid_cert = false; - { - let mut ap = ArgumentParser::new(); - ap.set_description("Run the echo client example"); - ap.refer(&mut server_host) - .add_option(&["--host"], Store, "Hostname of mumble server") - .required(); - ap.refer(&mut server_port) - .add_option(&["--port"], Store, "Port of mumble server"); - ap.refer(&mut username) - .add_option(&["--username"], Store, "User name used to connect"); - ap.refer(&mut accept_invalid_cert).add_option( - &["--accept-invalid-cert"], - StoreTrue, - "Accept invalid TLS certificates", - ); - ap.parse_args_or_exit(); - } + setup_logger(); // Oneshot channel for setting UDP CryptState from control task // For simplicity we don't deal with re-syncing, real applications would have to. let (crypt_state_sender, crypt_state_receiver) = mpsc::channel::(1); // crypt state should always be consumed before sending a new one let (packet_sender, packet_receiver) = mpsc::unbounded_channel::>(); - let (command_sender, command_receiver) = mpsc::unbounded_channel::(); - let (command_response_sender, command_response_receiver) = - mpsc::unbounded_channel::, ()>>(); + let (command_sender, command_receiver) = mpsc::unbounded_channel::<(Command, IpcSender, ()>>)>(); let (connection_info_sender, connection_info_receiver) = watch::channel::>(None); let state = State::new( packet_sender, - command_sender.clone(), connection_info_sender, ); let state = Arc::new(Mutex::new(state)); // Run it - join!( + let (_, _, _, e) = join!( network::tcp::handle( Arc::clone(&state), connection_info_receiver.clone(), @@ -104,38 +50,29 @@ async fn main() { connection_info_receiver.clone(), crypt_state_receiver, ), - command::handle(state, command_receiver, command_response_sender,), - send_commands( - command_sender, - Command::ServerConnect { - host: server_host, - port: server_port, - username: username.clone(), - accept_invalid_cert - } + command::handle( + state, + command_receiver, ), - receive_command_responses(command_response_receiver,), + spawn_blocking(move || { + receive_oneshot_commands(command_sender); + }), ); + e.unwrap(); } -async fn send_commands(command_sender: mpsc::UnboundedSender, connect_command: Command) { - command_sender.send(connect_command.clone()).unwrap(); - tokio::time::delay_for(Duration::from_secs(2)).await; - command_sender.send(Command::ServerDisconnect).unwrap(); - tokio::time::delay_for(Duration::from_secs(2)).await; - command_sender.send(connect_command.clone()).unwrap(); - tokio::time::delay_for(Duration::from_secs(2)).await; - command_sender.send(Command::ServerDisconnect).unwrap(); - - debug!("Finished sending commands"); -} - -async fn receive_command_responses( - mut command_response_receiver: mpsc::UnboundedReceiver, ()>>, +fn receive_oneshot_commands( + command_sender: mpsc::UnboundedSender<(Command, IpcSender, ()>>)>, ) { - while let Some(command_response) = command_response_receiver.recv().await { - debug!("{:?}", command_response); - } + loop { + // create listener + let (server, server_name): (IpcOneShotServer<(Command, IpcSender, ()>>)>, String) = IpcOneShotServer::new().unwrap(); + fs::write("/var/tmp/mumd-oneshot", &server_name).unwrap(); + debug!("Listening for command at {}...", server_name); - debug!("Finished receiving commands"); + // receive command and response channel + let (_, conn): (_, (Command, IpcSender, ()>>)) = 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 b6fe780..0fd814c 100644 --- a/mumd/src/state.rs +++ b/mumd/src/state.rs @@ -1,12 +1,12 @@ use crate::audio::Audio; -use crate::command::{Command, CommandResponse}; use crate::network::ConnectionInfo; + use log::*; use mumble_protocol::control::msgs; use mumble_protocol::control::ControlPacket; use mumble_protocol::voice::Serverbound; -use std::collections::hash_map::Entry; -use std::collections::HashMap; +use mumlib::command::{Command, CommandResponse}; +use mumlib::state::Server; use std::net::ToSocketAddrs; use tokio::sync::{mpsc, watch}; @@ -22,7 +22,6 @@ pub struct State { audio: Audio, packet_sender: mpsc::UnboundedSender>, - command_sender: mpsc::UnboundedSender, connection_info_sender: watch::Sender>, phase_watcher: (watch::Sender, watch::Receiver), @@ -34,14 +33,12 @@ pub struct State { impl State { pub fn new( packet_sender: mpsc::UnboundedSender>, - command_sender: mpsc::UnboundedSender, connection_info_sender: watch::Sender>, ) -> Self { Self { server: None, audio: Audio::new(), packet_sender, - command_sender, connection_info_sender, phase_watcher: watch::channel(StatePhase::Disconnected), username: None, @@ -74,7 +71,7 @@ impl State { ( false, Ok(Some(CommandResponse::ChannelList { - channels: self.server.as_ref().unwrap().channels.clone(), + channels: self.server.as_ref().unwrap().channels().clone(), })), ) } @@ -190,215 +187,3 @@ impl State { self.username.as_ref() } } - -#[derive(Clone, Debug)] -pub struct Server { - channels: HashMap, - users: HashMap, - pub welcome_text: Option, -} - -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 { - &self.channels - } - - pub fn users(&self) -> &HashMap { - &self.users - } -} - -#[derive(Clone, Debug)] -pub struct Channel { - description: Option, - links: Vec, - max_users: u32, - name: String, - parent: Option, - 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 - } -} - -#[derive(Clone, Debug)] -pub struct User { - channel: u32, - comment: Option, - hash: Option, - 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 - } -} -- cgit v1.2.1 From 47d3834a6e5b82e287b975fbf55939c6fd44ca02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Thu, 15 Oct 2020 21:02:44 +0200 Subject: add mumlib --- mumlib/Cargo.toml | 15 ++++ mumlib/src/command.rs | 31 ++++++++ mumlib/src/lib.rs | 35 ++++++++ mumlib/src/state.rs | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 298 insertions(+) create mode 100644 mumlib/Cargo.toml create mode 100644 mumlib/src/command.rs create mode 100644 mumlib/src/lib.rs create mode 100644 mumlib/src/state.rs diff --git a/mumlib/Cargo.toml b/mumlib/Cargo.toml new file mode 100644 index 0000000..a2627d4 --- /dev/null +++ b/mumlib/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mumlib" +version = "0.1.0" +authors = ["Gustav Sörnäs ", + "Eskil Queseth "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +colored = "2.0" +fern = "0.5" +log = "0.4" +mumble-protocol = "0.3" +serde = { version = "1.0", features = ["derive"] } diff --git a/mumlib/src/command.rs b/mumlib/src/command.rs new file mode 100644 index 0000000..483d8c6 --- /dev/null +++ b/mumlib/src/command.rs @@ -0,0 +1,31 @@ +use crate::state::{Channel, Server}; + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum Command { + ChannelJoin { + channel_id: u32, + }, + ChannelList, + ServerConnect { + host: String, + port: u16, + username: String, + accept_invalid_cert: bool, //TODO ask when connecting + }, + ServerDisconnect, + Status, +} + +#[derive(Debug, Deserialize, Serialize)] +pub enum CommandResponse { + ChannelList { + channels: HashMap, + }, + Status { + username: Option, + server_state: Server, + }, +} diff --git a/mumlib/src/lib.rs b/mumlib/src/lib.rs new file mode 100644 index 0000000..ebf2019 --- /dev/null +++ b/mumlib/src/lib.rs @@ -0,0 +1,35 @@ +pub mod command; +pub mod state; + +use colored::*; +use log::*; + +pub fn setup_logger() { + fern::Dispatch::new() + .format(|out, message, record| { + let message = message.to_string(); + out.finish(format_args!( + "{} {}:{}{}{}", + //TODO runtime flag that disables color + match record.level() { + Level::Error => "ERROR".red(), + Level::Warn => "WARN ".yellow(), + Level::Info => "INFO ".normal(), + Level::Debug => "DEBUG".green(), + Level::Trace => "TRACE".normal(), + }, + record.file().unwrap(), + record.line().unwrap(), + if message.chars().any(|e| e == '\n') { + "\n" + } else { + " " + }, + message + )) + }) + .level(log::LevelFilter::Debug) + .chain(std::io::stderr()) + .apply() + .unwrap(); +} diff --git a/mumlib/src/state.rs b/mumlib/src/state.rs new file mode 100644 index 0000000..f90634e --- /dev/null +++ b/mumlib/src/state.rs @@ -0,0 +1,217 @@ +use log::*; +use mumble_protocol::control::msgs; +use serde::{Deserialize, Serialize}; +use std::collections::hash_map::Entry; +use std::collections::HashMap; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Server { + channels: HashMap, + users: HashMap, + pub welcome_text: Option, +} + +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 { + &self.channels + } + + pub fn users(&self) -> &HashMap { + &self.users + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Channel { + description: Option, + links: Vec, + max_users: u32, + name: String, + parent: Option, + 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 + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct User { + channel: u32, + comment: Option, + hash: Option, + 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 + } +} -- cgit v1.2.1 From 8ac1622a4602e6f5eb9a4e4be4ac2e5ea89b4c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Thu, 15 Oct 2020 21:23:58 +0200 Subject: minor clean --- mumctl/src/main.rs | 2 -- todo.org | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 403447f..39986f0 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -7,8 +7,6 @@ use std::fs; fn main() { setup_logger(); - // MUMCTL - //temp send command and channel to listener debug!("Creating channel"); let (tx_client, rx_client): (IpcSender, ()>>, IpcReceiver, ()>>) = ipc::channel().unwrap(); diff --git a/todo.org b/todo.org index b8f9ff9..638e6f1 100644 --- a/todo.org +++ b/todo.org @@ -1,5 +1,5 @@ * Current -** Få igång mumctl :gustav: +** Städa mumctl * Next ** Flytta ljudgrejer till client state -- cgit v1.2.1 From 5fee8b205bf9f46a038f55da51b3cdb68a0ab429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Thu, 15 Oct 2020 22:34:00 +0200 Subject: remove todo.org moved to gitlab issues --- todo.org | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 todo.org diff --git a/todo.org b/todo.org deleted file mode 100644 index 638e6f1..0000000 --- a/todo.org +++ /dev/null @@ -1,26 +0,0 @@ -* Current -** Städa mumctl - -* Next -** Flytta ljudgrejer till client state -** Split audio input/output -** Refaktorera TCP-paket -** Hantera fler TCP-paket -** Tolka diffs när nytt state kommer in -** Lägg till ett sätt att stänga av daemonen - -* Later -** Kombinera flera ljudkällor rimligt (utan clipping) -Compression. https://docs.rs/compressor/0.3.0/compressor/ -** UDP crypt state resync?? Kommentar från exemplet -Se Mumble.proto: CryptSetup. Både server och klient kan skicka en tom request -för att skaffa en ny crypt. -** Coolare invalid cert-check -Fråga användaren vad som ska hända om ett invalid cert dyker upp istället för -att ha det som en flagga vid start -** Mindre pub -Urk -** Ljudvolymen -Via kommando, inget automatiskt -** Bättre error handling -** Hantera resterande TCP-paket -- cgit v1.2.1 From 402174bc195fd59adcee82a9b2d2b3034320406b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Fri, 16 Oct 2020 00:21:05 +0200 Subject: initial clap --- mumctl/Cargo.toml | 2 +- mumctl/src/main.rs | 69 ++++++++++++++++++++++++++++++++++++++++++--------- mumlib/src/command.rs | 2 +- usage.org | 12 ++++----- 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/mumctl/Cargo.toml b/mumctl/Cargo.toml index 3a432d9..1f2f727 100644 --- a/mumctl/Cargo.toml +++ b/mumctl/Cargo.toml @@ -11,8 +11,8 @@ edition = "2018" [dependencies] mumlib = { path = "../mumlib" } +clap = { version = "2.33", features = ["yaml"] } log = "0.4" ipc-channel = "0.14" -#clap = "2.33" #cursive = "0.15" diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 39986f0..e4746d3 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -1,3 +1,4 @@ +use clap::{App, Arg, SubCommand}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use log::*; use mumlib::command::{Command, CommandResponse}; @@ -7,25 +8,69 @@ use std::fs; fn main() { setup_logger(); + let matches = App::new("mumctl") + .subcommand(SubCommand::with_name("server") + .subcommand(SubCommand::with_name("connect") + .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") + .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")) + .get_matches(); + + let command = + if let Some(matches) = matches.subcommand_matches("server") { + if let Some(matches) = matches.subcommand_matches("connect") { + let host = matches.value_of("host").unwrap(); + let username = matches.value_of("username").unwrap(); + Some(Command::ServerConnect { + host: host.to_string(), + port: 64738u16, //TODO + username: username.to_string(), + accept_invalid_cert: true, //TODO + }) + } else { + None + } + } else if let Some(matches) = matches.subcommand_matches("channel") { + if let Some(matches) = matches.subcommand_matches("list") { + if matches.is_present("short") { + None //TODO + } else { + None //TODO + } + } else if let Some(_matches) = matches.subcommand_matches("connect") { + None //TODO + } else { + None + } + } else if let Some(_matches) = matches.subcommand_matches("status") { + None //TODO + } else { + None + }; + debug!("Creating channel"); let (tx_client, rx_client): (IpcSender, ()>>, IpcReceiver, ()>>) = ipc::channel().unwrap(); let server_name = fs::read_to_string("/var/tmp/mumd-oneshot").unwrap(); //TODO don't panic - debug!("Connecting to mumd at {}", server_name); + info!("Sending {:#?}", command); let tx0 = IpcSender::connect(server_name).unwrap(); - let connect_command = Command::ServerConnect { - host: "10.0.0.10".to_string(), - port: 64738u16, - username: "gustav-mumd".to_string(), - accept_invalid_cert: true, - }; - debug!("Sending {:?} to mumd", connect_command); - tx0.send(( - connect_command, - tx_client)) .unwrap(); + tx0.send((command.unwrap(), tx_client)).unwrap(); debug!("Reading response"); let response = rx_client.recv().unwrap(); - debug!("{:?}", response); + debug!("\n{:#?}", response); } diff --git a/mumlib/src/command.rs b/mumlib/src/command.rs index 483d8c6..b2ac321 100644 --- a/mumlib/src/command.rs +++ b/mumlib/src/command.rs @@ -13,7 +13,7 @@ pub enum Command { host: String, port: u16, username: String, - accept_invalid_cert: bool, //TODO ask when connecting + accept_invalid_cert: bool, }, ServerDisconnect, Status, diff --git a/usage.org b/usage.org index f866dca..e340165 100644 --- a/usage.org +++ b/usage.org @@ -20,8 +20,8 @@ The daemon doesn't do anything by itself. Interfacing with it is done through The basic commands are the smallest subset of commands that allow the user to actually use mum for something. In this case it means connecting to a server, listing channels and connecting to channels. -** TODO server -*** TODO connect +** DONE server +*** DONE connect #+BEGIN_SRC bash $ mumctl server connect localhost your_name connecting to localhost... @@ -31,8 +31,8 @@ root │ some person | your_name #+END_SRC -** TODO channel -*** TODO list +** DONE channel +*** DONE list #+BEGIN_SRC bash $ mumctl channel list root [3](4) @@ -60,13 +60,13 @@ root [3](4) │ └─ subsubchannel [2] └─ AFK [1] #+END_SRC -*** TODO connect +*** DONE connect #+BEGIN_SRC bash $ mumctl channel connect some channel connecting to some channel... connected #+END_SRC -** TODO status +** DONE status #+BEGIN_SRC bash $ mumctl status connected to localhost:65837 as your_name -- cgit v1.2.1 From 109b7eb4ef01b250b2aae6d9218ba6f629f33042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Fri, 16 Oct 2020 00:30:12 +0200 Subject: clap set arg required else help --- mumctl/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index e4746d3..f2defc6 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -1,4 +1,4 @@ -use clap::{App, Arg, SubCommand}; +use clap::{App, AppSettings, Arg, SubCommand}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use log::*; use mumlib::command::{Command, CommandResponse}; @@ -9,6 +9,7 @@ fn main() { setup_logger(); let matches = App::new("mumctl") + .setting(AppSettings::ArgRequiredElseHelp) .subcommand(SubCommand::with_name("server") .subcommand(SubCommand::with_name("connect") .arg(Arg::with_name("host") -- cgit v1.2.1 From 91fbdd888ec5b8356f7278150eebd1a8b980a134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Fri, 16 Oct 2020 00:32:55 +0200 Subject: more set arg required else help --- mumctl/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index f2defc6..c6339f7 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -11,7 +11,9 @@ fn main() { let matches = 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)) @@ -20,6 +22,7 @@ fn main() { .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") -- cgit v1.2.1 From 857a3614f0aac2510717799708760a4124d412c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Fri, 16 Oct 2020 00:45:45 +0200 Subject: update logging --- mumctl/src/main.rs | 11 +++++++---- mumd/src/main.rs | 7 +++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index c6339f7..e8ac47f 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -7,6 +7,7 @@ use std::fs; fn main() { setup_logger(); + debug!("Logger up!"); let matches = App::new("mumctl") .setting(AppSettings::ArgRequiredElseHelp) @@ -33,6 +34,7 @@ fn main() { .subcommand(SubCommand::with_name("status")) .get_matches(); + debug!("Matching clap"); let command = if let Some(matches) = matches.subcommand_matches("server") { if let Some(matches) = matches.subcommand_matches("connect") { @@ -64,17 +66,18 @@ fn main() { } else { None }; + debug!("Matched {:#?}", &command); - debug!("Creating channel"); + debug!("Creating CommandResponse-channel"); let (tx_client, rx_client): (IpcSender, ()>>, IpcReceiver, ()>>) = ipc::channel().unwrap(); let server_name = fs::read_to_string("/var/tmp/mumd-oneshot").unwrap(); //TODO don't panic - info!("Sending {:#?}", command); + info!("Sending {:#?}\n to {}", command, server_name); let tx0 = IpcSender::connect(server_name).unwrap(); tx0.send((command.unwrap(), tx_client)).unwrap(); - debug!("Reading response"); + debug!("Waiting for response"); let response = rx_client.recv().unwrap(); - debug!("\n{:#?}", response); + debug!("Received {:#?}", response); } diff --git a/mumd/src/main.rs b/mumd/src/main.rs index 8639c35..14a43c1 100644 --- a/mumd/src/main.rs +++ b/mumd/src/main.rs @@ -37,7 +37,6 @@ async fn main() { ); let state = Arc::new(Mutex::new(state)); - // Run it let (_, _, _, e) = join!( network::tcp::handle( Arc::clone(&state), @@ -54,7 +53,7 @@ async fn main() { state, command_receiver, ), - spawn_blocking(move || { + spawn_blocking(move || { // IpcSender is blocking receive_oneshot_commands(command_sender); }), ); @@ -68,11 +67,11 @@ fn receive_oneshot_commands( // create listener let (server, server_name): (IpcOneShotServer<(Command, IpcSender, ()>>)>, String) = IpcOneShotServer::new().unwrap(); fs::write("/var/tmp/mumd-oneshot", &server_name).unwrap(); - debug!("Listening for command at {}...", server_name); + debug!("Listening to {}", server_name); // receive command and response channel let (_, conn): (_, (Command, IpcSender, ()>>)) = server.accept().unwrap(); - debug!("Sending command {:?} to command handler", conn.0); + debug!("Sending to command handler: {:#?}", conn.0); command_sender.send(conn).unwrap(); } } -- cgit v1.2.1 From 1869caa25a295194a0cef26b7df3c20ef69531ef Mon Sep 17 00:00:00 2001 From: Eskil Queseth Date: Fri, 16 Oct 2020 00:54:15 +0200 Subject: implement rest of commands --- mumctl/src/main.rs | 70 ++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index e8ac47f..ebd09bf 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -35,49 +35,47 @@ fn main() { .get_matches(); debug!("Matching clap"); - let command = - if let Some(matches) = matches.subcommand_matches("server") { - if let Some(matches) = matches.subcommand_matches("connect") { - let host = matches.value_of("host").unwrap(); - let username = matches.value_of("username").unwrap(); - Some(Command::ServerConnect { - host: host.to_string(), - port: 64738u16, //TODO - username: username.to_string(), - accept_invalid_cert: true, //TODO - }) - } else { - None - } - } else if let Some(matches) = matches.subcommand_matches("channel") { - if let Some(matches) = matches.subcommand_matches("list") { - if matches.is_present("short") { - None //TODO - } else { - None //TODO - } - } else if let Some(_matches) = matches.subcommand_matches("connect") { + if let Some(matches) = matches.subcommand_matches("server") { + if let Some(matches) = matches.subcommand_matches("connect") { + let host = matches.value_of("host").unwrap(); + let username = matches.value_of("username").unwrap(); + send_command(Command::ServerConnect { + host: host.to_string(), + port: 64738u16, //TODO + username: username.to_string(), + accept_invalid_cert: true, //TODO + }).unwrap(); + } else if let Some(_) = matches.subcommand_matches("disconnect") { + send_command(Command::ServerDisconnect).unwrap(); + } + } else if let Some(matches) = matches.subcommand_matches("channel") { + if let Some(matches) = matches.subcommand_matches("list") { + let res = send_command(Command::ChannelList).unwrap().unwrap(); + println!("{:#?}", res); + /*if matches.is_present("short") { None //TODO } else { - None - } - } else if let Some(_matches) = matches.subcommand_matches("status") { - None //TODO - } else { - None - }; - debug!("Matched {:#?}", &command); + None //TODO + };*/ + } else if let Some(matches) = matches.subcommand_matches("connect") { + send_command(Command::ChannelJoin { + channel_id: matches.value_of("channel").unwrap().parse::().unwrap() + }).unwrap(); + } + } else if let Some(matches) = matches.subcommand_matches("status") { + let res = send_command(Command::Status).unwrap().unwrap(); + println!("{:#?}", res); + }; +} - debug!("Creating CommandResponse-channel"); +fn send_command(command: Command) -> Result, ()> { let (tx_client, rx_client): (IpcSender, ()>>, IpcReceiver, ()>>) = ipc::channel().unwrap(); let server_name = fs::read_to_string("/var/tmp/mumd-oneshot").unwrap(); //TODO don't panic - info!("Sending {:#?}\n to {}", command, server_name); + let tx0 = IpcSender::connect(server_name).unwrap(); - tx0.send((command.unwrap(), tx_client)).unwrap(); + tx0.send((command, tx_client)).unwrap(); - debug!("Waiting for response"); - let response = rx_client.recv().unwrap(); - debug!("Received {:#?}", response); + rx_client.recv().unwrap() } -- cgit v1.2.1 From 5c3dd13cf344c51af145e324daa7103eec0cb8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Fri, 16 Oct 2020 01:07:35 +0200 Subject: optionally generate shell completions --- mumctl/src/main.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index ebd09bf..41c7306 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -1,15 +1,15 @@ -use clap::{App, AppSettings, Arg, SubCommand}; +use clap::{App, AppSettings, Arg, Shell, SubCommand}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use log::*; use mumlib::command::{Command, CommandResponse}; use mumlib::setup_logger; -use std::fs; +use std::{fs, io}; fn main() { setup_logger(); debug!("Logger up!"); - let matches = App::new("mumctl") + let mut app = App::new("mumctl") .setting(AppSettings::ArgRequiredElseHelp) .subcommand(SubCommand::with_name("server") .setting(AppSettings::ArgRequiredElseHelp) @@ -32,7 +32,9 @@ fn main() { .arg(Arg::with_name("channel") .required(true)))) .subcommand(SubCommand::with_name("status")) - .get_matches(); + .subcommand(SubCommand::with_name("completions")); + + let matches = app.clone().get_matches(); debug!("Matching clap"); if let Some(matches) = matches.subcommand_matches("server") { @@ -49,7 +51,7 @@ fn main() { send_command(Command::ServerDisconnect).unwrap(); } } else if let Some(matches) = matches.subcommand_matches("channel") { - if let Some(matches) = matches.subcommand_matches("list") { + if let Some(_matches) = matches.subcommand_matches("list") { let res = send_command(Command::ChannelList).unwrap().unwrap(); println!("{:#?}", res); /*if matches.is_present("short") { @@ -62,9 +64,24 @@ fn main() { channel_id: matches.value_of("channel").unwrap().parse::().unwrap() }).unwrap(); } - } else if let Some(matches) = matches.subcommand_matches("status") { + } else if let Some(_matches) = matches.subcommand_matches("status") { let res = send_command(Command::Status).unwrap().unwrap(); println!("{:#?}", res); + } 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; }; } -- cgit v1.2.1 From d35c9171271110339504abd96065dc25e1290500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Fri, 16 Oct 2020 01:12:49 +0200 Subject: add args for other shells --- mumctl/src/main.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 41c7306..124cc8c 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -32,7 +32,13 @@ fn main() { .arg(Arg::with_name("channel") .required(true)))) .subcommand(SubCommand::with_name("status")) - .subcommand(SubCommand::with_name("completions")); + .subcommand(SubCommand::with_name("completions") + .arg(Arg::with_name("zsh") + .long("zsh")) + .arg(Arg::with_name("bash") + .long("bash")) + .arg(Arg::with_name("fish") + .long("fish"))); let matches = app.clone().get_matches(); -- cgit v1.2.1