client/common: support new login screen

This commit is contained in:
2026-05-05 05:39:45 +00:00
committed by Sam Sartor
parent bc20cf825d
commit ffb109d3a0
10 changed files with 65 additions and 30 deletions
+7 -1
View File
@@ -21,10 +21,16 @@ pub struct AudioSettings {
pub denoise: bool,
}
#[derive(Debug, Clone)]
pub enum ConnectTarget {
Direct { host: String, port: u16 },
Proxy(String),
}
#[derive(Debug)]
pub enum Command {
Connect {
address: String,
target: ConnectTarget,
username: String,
config: ProxyOverrides,
},
+12 -6
View File
@@ -1,5 +1,5 @@
use crate::app::{Command, SharedState};
use color_eyre::eyre::Error;
use crate::app::{Command, ConnectTarget, SharedState};
use color_eyre::eyre::{bail, Error};
use futures_channel::mpsc::UnboundedReceiver;
use mumble_protocol::control::ClientControlCodec;
use std::net::ToSocketAddrs;
@@ -70,7 +70,7 @@ impl ServerCertVerifier for NoCertificateVerification {
#[instrument]
pub async fn network_connect(
address: String,
target: ConnectTarget,
username: String,
event_rx: &mut UnboundedReceiver<Command>,
overrides: &ProxyOverrides,
@@ -78,6 +78,13 @@ pub async fn network_connect(
) -> Result<(), Error> {
info!("connecting");
let (host, port) = match target {
ConnectTarget::Direct { host, port } => (host, port),
ConnectTarget::Proxy(_) => {
bail!("desktop/mobile platform requires a direct host:port, not a proxy URL")
}
};
let config = ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoCertificateVerification))
@@ -85,15 +92,14 @@ pub async fn network_connect(
let connector = TlsConnector::from(Arc::new(config));
let addr = format!("{}:{}", address, 64738)
let addr = (&*host, port)
.to_socket_addrs()?
.next()
.unwrap();
let server_tcp = TcpStream::connect(addr).await?;
let server_stream = connector
//.connect("127.0.0.1".try_into()?, server_tcp)
.connect(address.try_into()?, server_tcp)
.connect(host.try_into()?, server_tcp)
.await?;
let (read_server, write_server) = tokio::io::split(server_stream);
+8 -4
View File
@@ -1,4 +1,4 @@
use crate::app::{Command, SharedState};
use crate::app::{Command, ConnectTarget, SharedState};
use color_eyre::eyre::Error;
use futures_channel::mpsc::UnboundedReceiver;
use mumble_web2_common::{ProxyOverrides, ServerStatus};
@@ -24,20 +24,24 @@ impl super::PlatformInterface for DesktopPlatform {
}
async fn network_connect(
address: String,
target: ConnectTarget,
username: String,
event_rx: &mut UnboundedReceiver<Command>,
overrides: &ProxyOverrides,
state: SharedState,
) -> Result<(), Error> {
super::connect::network_connect(address, username, event_rx, overrides, state).await
super::connect::network_connect(target, username, event_rx, overrides, state).await
}
async fn get_status(
_client: &reqwest::Client,
address: &str,
) -> color_eyre::Result<ServerStatus> {
mumble_web2_common::ping_server(address, 64738).await
let (host, port) = match address.rsplit_once(':') {
Some((h, p)) => (h, p.parse().unwrap_or(64738)),
None => (address, 64738),
};
mumble_web2_common::ping_server(host, port).await
}
fn init_logging() {
+8 -4
View File
@@ -1,4 +1,4 @@
use crate::app::{Command, SharedState};
use crate::app::{Command, ConnectTarget, SharedState};
use color_eyre::eyre::Error;
use futures_channel::mpsc::UnboundedReceiver;
use mumble_web2_common::{ProxyOverrides, ServerStatus};
@@ -20,20 +20,24 @@ impl super::PlatformInterface for MobilePlatform {
}
async fn network_connect(
address: String,
target: ConnectTarget,
username: String,
event_rx: &mut UnboundedReceiver<Command>,
overrides: &ProxyOverrides,
state: SharedState,
) -> Result<(), Error> {
super::connect::network_connect(address, username, event_rx, overrides, state).await
super::connect::network_connect(target, username, event_rx, overrides, state).await
}
async fn get_status(
_client: &reqwest::Client,
address: &str,
) -> color_eyre::Result<ServerStatus> {
mumble_web2_common::ping_server(address, 64738).await
let (host, port) = match address.rsplit_once(':') {
Some((h, p)) => (h, p.parse().unwrap_or(64738)),
None => (address, 64738),
};
mumble_web2_common::ping_server(host, port).await
}
fn init_logging() {
+2 -2
View File
@@ -4,7 +4,7 @@
//! The traits make the platform boundary explicit and provide compile-time verification.
#![allow(async_fn_in_trait)]
use crate::app::{Command, SharedState};
use crate::app::{Command, ConnectTarget, SharedState};
use crate::effects::AudioProcessor;
use color_eyre::eyre::Error;
use futures_channel::mpsc::UnboundedReceiver;
@@ -79,7 +79,7 @@ pub trait PlatformInterface {
/// Establish a connection to the Mumble server and run the network loop.
fn network_connect(
address: String,
target: ConnectTarget,
username: String,
event_rx: &mut UnboundedReceiver<Command>,
proxy_overrides: &ProxyOverrides,
+2 -6
View File
@@ -28,12 +28,8 @@ impl super::ConfigSystemInterface for NativeConfigSystem {
match serde_json::from_value::<T>(value_untyped) {
Ok(v) => Some(v),
Err(_) => {
let default_value = config_get_default(key)
.expect("Default value required after config parse failure");
Some(
serde_json::from_value::<T>(default_value)
.expect("Default value could not be parsed"),
)
let default_value = config_get_default(key)?;
serde_json::from_value::<T>(default_value).ok()
}
}
}
+5 -2
View File
@@ -1,6 +1,9 @@
/// Stub implementation of the platform interface, so that we can
/// `cargo check` without any --feature flags.
use crate::{app::SharedState, effects::AudioProcessor};
use crate::{
app::{ConnectTarget, SharedState},
effects::AudioProcessor,
};
use color_eyre::eyre::Error;
use futures_channel::mpsc::UnboundedReceiver;
use mumble_web2_common::{ProxyOverrides, ServerStatus};
@@ -21,7 +24,7 @@ impl super::PlatformInterface for StubPlatform {
}
fn network_connect(
_address: String,
_target: ConnectTarget,
_username: String,
_event_rx: &mut UnboundedReceiver<crate::app::Command>,
_overrides: &ProxyOverrides,
+9 -3
View File
@@ -1,4 +1,4 @@
use crate::app::{Command, SharedState};
use crate::app::{Command, ConnectTarget, SharedState};
use crate::effects::{AudioProcessor, AudioProcessorSender, TransmitState};
use color_eyre::eyre::{bail, eyre, Error};
use crossbeam::atomic::AtomicCell;
@@ -108,13 +108,19 @@ impl super::PlatformInterface for WebPlatform {
}
async fn network_connect(
address: String,
target: ConnectTarget,
username: String,
event_rx: &mut UnboundedReceiver<Command>,
overrides: &ProxyOverrides,
state: SharedState,
) -> Result<(), Error> {
network_connect(address, username, event_rx, overrides, state).await
let url = match target {
ConnectTarget::Proxy(url) => url,
ConnectTarget::Direct { .. } => {
bail!("web platform requires a proxy URL, not a direct host:port")
}
};
network_connect(url, username, event_rx, overrides, state).await
}
async fn get_status(
+2 -2
View File
@@ -39,7 +39,7 @@ use crate::imp::{
pub async fn network_entrypoint(mut event_rx: UnboundedReceiver<Command>, state: SharedState) {
loop {
let Some(Command::Connect {
address,
target,
username,
config,
}) = event_rx.next().await
@@ -50,7 +50,7 @@ pub async fn network_entrypoint(mut event_rx: UnboundedReceiver<Command>, state:
*state.server.write_unchecked() = Default::default();
*state.status.write_unchecked() = ConnectionState::Connecting;
if let Err(error) =
Platform::network_connect(address, username, &mut event_rx, &config, state.clone())
Platform::network_connect(target, username, &mut event_rx, &config, state.clone())
.await
{
error!("could not connect {:?}", error);
+10
View File
@@ -17,6 +17,16 @@ pub struct ServerStatus {
pub bandwidth: Option<u32>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)]
pub struct ServerEntry {
pub name: String,
pub address: String,
pub port: u16,
pub username: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub password: Option<String>,
}
/// Mumble UDP ping protocol.
///
/// Send a 12-byte packet: 4 zero bytes + 8-byte identifier.