Skip to main content

mumcord_protocol/
client.rs

1//! The control-channel connection: login, background read/write tasks, the event stream and the
2//! command API.
3
4use std::collections::{BTreeMap, VecDeque};
5use std::net::SocketAddr;
6use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
7use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
8use std::time::{Duration, Instant};
9
10use bytes::{Bytes, BytesMut};
11use prost::Message;
12use rustls::pki_types::ServerName;
13use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadHalf};
14use tokio::net::TcpStream;
15use tokio::sync::{Notify, mpsc, oneshot, watch};
16use tokio_rustls::TlsConnector;
17use tokio_rustls::client::TlsStream;
18
19use crate::error::{CommandError, ConnectError, PermissionDenied, ProtocolError, RejectReason};
20use crate::framing::{self, Frame, MessageType};
21use crate::proto;
22use crate::ratelimit::{self, LOW_PRIORITY_MAX_WAIT, LeakyBucket, Limit};
23use crate::state::{Channel, CryptSetup, ServerInfo, ServerState, User, UserChange};
24use crate::tap::{PacketTap, TapDirection, TapPacing, TappedFrame};
25use crate::tls::{self, CertVerifier, ClientIdentity, ServerCert};
26use crate::version::MumbleVersion;
27use crate::voice::CryptStats;
28
29/// Interval of TCP keep-alive pings; Mumble's own client uses the same.
30const PING_INTERVAL: Duration = Duration::from_secs(5);
31/// Servers drop clients after 30 s of silence; if we hear nothing for that long the link is dead.
32const READ_TIMEOUT: Duration = Duration::from_secs(30);
33/// How long a requested disconnect may spend sending what is still queued. After the burst of 4,
34/// paced messages go out one per 1.2 s, so this covers a burst plus one more; a longer wait would
35/// hold up quitting the app for messages that are rarely that many.
36const DRAIN_TIMEOUT: Duration = Duration::from_secs(2);
37
38/// Everything needed to open a connection.
39pub struct ConnectConfig {
40    pub host: String,
41    pub port: u16,
42    pub username: String,
43    /// Server or user password.
44    pub password: Option<String>,
45    /// Access tokens (channel passwords) the server matches against `#token` ACL groups.
46    pub tokens: Vec<String>,
47    /// Client certificate; without one the user cannot register.
48    pub identity: Option<ClientIdentity>,
49    /// Trust decision for the server certificate (see [`crate::tls::ServerCert`]).
50    pub verify_cert: CertVerifier,
51    /// Upper bound for TCP connect + TLS + login.
52    pub login_timeout: Duration,
53}
54
55/// Where a text message goes (or came to). Any combination is allowed.
56#[derive(Debug, Clone, Default, PartialEq, Eq)]
57pub struct TextTarget {
58    /// Private recipients by session.
59    pub sessions: Vec<u32>,
60    pub channels: Vec<u32>,
61    /// Channels including all sub-channels.
62    pub trees: Vec<u32>,
63}
64
65impl TextTarget {
66    pub fn channel(id: u32) -> Self {
67        Self {
68            channels: vec![id],
69            ..Default::default()
70        }
71    }
72
73    pub fn user(session: u32) -> Self {
74        Self {
75            sessions: vec![session],
76            ..Default::default()
77        }
78    }
79}
80
81/// A received text message. `message` is server-provided HTML and must be sanitized.
82#[derive(Debug, Clone, PartialEq)]
83pub struct TextMessage {
84    /// Sender session; `None` for messages from the server itself.
85    pub actor: Option<u32>,
86    /// The sender as known when the message arrived, without comment and avatar. Events are
87    /// handled after the state moved on, so a sender who left right after sending is no longer
88    /// in the state by then; `None` for the server itself or an unknown session.
89    pub sender: Option<User>,
90    pub target: TextTarget,
91    pub message: String,
92}
93
94/// Largest plugin data payload a standard server forwards (murmur's `MAX_DATA_LENGTH`).
95pub const PLUGIN_DATA_MAX: usize = 1000;
96
97/// Data another client sent through the server's plugin data channel.
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct PluginData {
100    /// Sender session as stamped by the server.
101    pub sender: Option<u32>,
102    /// What the data is; receivers ignore ids they do not know.
103    pub data_id: String,
104    pub data: Bytes,
105}
106
107/// What became of plugin data sent with [`Handle::send_plugin_data_low`].
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum LowOutcome {
110    /// Written to the server.
111    Sent,
112    /// Dropped after waiting 10 s behind higher-priority plugin data.
113    Expired,
114    /// The connection ended before it was sent.
115    Disconnected,
116}
117
118/// Tells the sender of low-priority plugin data whether it went out. Dropping it is fine.
119#[derive(Debug)]
120pub struct PluginTicket(oneshot::Receiver<LowOutcome>);
121
122impl PluginTicket {
123    /// Waits until the data is sent or dropped.
124    pub async fn outcome(self) -> LowOutcome {
125        // The writer drops the sender only when the connection ends.
126        self.0.await.unwrap_or(LowOutcome::Disconnected)
127    }
128}
129
130/// Why the connection ended.
131#[derive(Debug, Clone, PartialEq, Eq)]
132pub enum DisconnectReason {
133    /// `Handle::disconnect` was called or all handles were dropped.
134    Requested,
135    /// The server removed us (kick or ban).
136    Kicked {
137        actor: Option<u32>,
138        reason: Option<String>,
139        ban: bool,
140    },
141    /// The server closed the connection.
142    Closed,
143    /// Nothing received for too long.
144    Timeout,
145    /// Network or protocol failure.
146    Error(String),
147}
148
149/// Changes after login. State is already updated when an event arrives.
150#[derive(Debug, Clone, PartialEq)]
151pub enum Event {
152    ChannelUpdated(Channel),
153    ChannelRemoved(Channel),
154    UserUpdated(UserChange),
155    UserRemoved {
156        user: User,
157        actor: Option<u32>,
158        reason: Option<String>,
159        ban: bool,
160    },
161    TextMessage(TextMessage),
162    /// A request was refused; the typed reason is in the payload.
163    PermissionDenied(PermissionDenied),
164    /// Effective permissions changed (see `ServerState::permissions`).
165    PermissionsUpdated {
166        channel_id: Option<u32>,
167        /// The server dropped every cached permission (`flush`), e.g. after an ACL or group
168        /// change: the client asks again for the channels it needs (#248).
169        flush: bool,
170    },
171    /// A channel's groups and ACL entries, the answer to [`Handle::query_acl`]. Standard servers
172    /// send it only to users with the Write permission; a refusal arrives as
173    /// [`Event::PermissionDenied`] with `permission == [`crate::permissions::WRITE`].
174    Acl(Box<proto::Acl>),
175    /// Names for registered user ids (or ids for names), the answer to [`Handle::query_users`].
176    /// Names are untrusted server input.
177    UsersQueried(proto::QueryUsers),
178    /// The server's ban list, the answer to [`Handle::query_ban_list`]. Standard servers send it
179    /// only to users with the Ban permission in the root channel, so a refusal arrives as
180    /// [`Event::PermissionDenied`] with `permission == [`crate::permissions::BAN`]. Texts and
181    /// addresses are untrusted server input (issue #254).
182    BanList(Vec<crate::ban::Ban>),
183    /// The server's registered accounts, the answer to [`Handle::query_user_list`] and every push
184    /// it sends after a change (issue #256). Standard servers send it only to users with the
185    /// Register permission in the root channel. `last_seen` and `last_channel` stay `None` on
186    /// servers that do not report them. Names are untrusted server text.
187    UserList(Vec<crate::registered::RegisteredUser>),
188    ServerConfigUpdated(ServerInfo),
189    /// UDP crypto parameters for the voice transport, passed through as received (also during
190    /// login; the latest value is in `ServerState::crypt`).
191    CryptSetup(CryptSetup),
192    /// A voice packet tunnelled through TCP (raw, unencrypted Mumble UDP packet).
193    UdpTunnel(Bytes),
194    /// Plugin data from another client (server 1.4+). `data` is untrusted and unvalidated.
195    PluginData(PluginData),
196    /// Connection figures of a person, the answer to [`Handle::request_user_stats`]. Which
197    /// fields are set depends on the own permissions; text fields are untrusted.
198    UserStats(Box<proto::UserStats>),
199    /// Final event; the handle stops working afterwards.
200    Disconnected(DisconnectReason),
201}
202
203/// A live connection: the command handle plus the event stream.
204pub struct Connection {
205    pub handle: Handle,
206    /// Unbounded so a slow consumer can never stall the reader and miss pings.
207    pub events: mpsc::UnboundedReceiver<Event>,
208    /// The server certificate seen during the TLS handshake, for the server info panel.
209    pub server_cert: Option<ServerCert>,
210}
211
212enum Outgoing {
213    Frame(Frame),
214    /// Plugin data that goes only when no other plugin data is waiting; see
215    /// [`Handle::send_plugin_data_low`].
216    LowPluginData(Frame, oneshot::Sender<LowOutcome>),
217    /// Answered once everything queued before it is written and the server has answered a ping
218    /// sent after it.
219    Flush(oneshot::Sender<()>),
220    /// Sends what is still queued (within [`DRAIN_TIMEOUT`]), then closes.
221    Disconnect,
222    /// Closes at once and drops anything queued: the connection is lost or the login failed.
223    Abort,
224}
225
226struct Shared {
227    state: Mutex<ServerState>,
228    shutdown: Notify,
229    /// Set by [`Handle::disconnect`]: no new commands are taken while the queue drains.
230    closing: AtomicBool,
231    /// Timestamp of the latest ping the server answered. The server handles a client's messages
232    /// in order, so an answered ping means everything sent before it has been processed.
233    pong: watch::Sender<u64>,
234    reason: Mutex<Option<DisconnectReason>>,
235    connected: AtomicBool,
236    ping: Mutex<PingReport>,
237    tap: Option<Arc<dyn PacketTap>>,
238    /// Low-priority plugin data dropped because it waited too long.
239    low_expired: AtomicU64,
240    /// The access tokens last sent to the server (login or [`Handle::set_access_tokens`]).
241    tokens: Mutex<Vec<String>>,
242}
243
244impl Shared {
245    fn state(&self) -> MutexGuard<'_, ServerState> {
246        self.state
247            .lock()
248            .unwrap_or_else(|poisoned| poisoned.into_inner())
249    }
250
251    fn ping(&self) -> MutexGuard<'_, PingReport> {
252        self.ping.lock().unwrap_or_else(|p| p.into_inner())
253    }
254
255    /// Records the first reason for ending the connection and wakes the reader.
256    fn shut_down(&self, reason: DisconnectReason) {
257        let mut slot = self.reason.lock().unwrap_or_else(|p| p.into_inner());
258        if slot.is_none() {
259            *slot = Some(reason);
260        }
261        self.shutdown.notify_one();
262    }
263
264    fn tap(&self, direction: TapDirection, frame: &Frame, pacing: Option<TapPacing>) {
265        if let Some(tap) = self.tap.as_ref().filter(|tap| tap.enabled()) {
266            tap.on_frame(TappedFrame::new(
267                direction,
268                frame.kind,
269                &frame.payload,
270                pacing,
271            ));
272        }
273    }
274}
275
276/// Own ping figures, sent along with every keep-alive ping. The server keeps only what each
277/// client reports about itself and hands that out in UserStats, so without them everyone else
278/// sees no ping for this user (FR-2.25).
279struct PingReport {
280    started: Instant,
281    tcp_packets: u32,
282    tcp_avg_ms: f64,
283    /// Sum of squared deviations from the mean (Welford), for the variance.
284    tcp_m2: f64,
285    udp: Option<UdpPing>,
286    /// Voice packets this client received, which the server shows others as "from client".
287    crypt: CryptStats,
288}
289
290/// Smoothed UDP ping of the voice link, which runs apart from the control connection.
291#[derive(Debug, Clone, Copy, PartialEq)]
292pub struct UdpPing {
293    pub avg_ms: f32,
294    /// UDP packets received so far.
295    pub packets: u32,
296}
297
298impl Default for PingReport {
299    fn default() -> Self {
300        Self {
301            started: Instant::now(),
302            tcp_packets: 0,
303            tcp_avg_ms: 0.0,
304            tcp_m2: 0.0,
305            udp: None,
306            crypt: CryptStats::default(),
307        }
308    }
309}
310
311impl PingReport {
312    /// Microseconds like Mumble: a local server answers well below one millisecond.
313    fn now_us(&self) -> u64 {
314        self.started.elapsed().as_micros() as u64
315    }
316
317    fn on_reply(&mut self, sent_us: u64) {
318        let Some(rtt_us) = self.now_us().checked_sub(sent_us) else {
319            return;
320        };
321        let rtt_ms = rtt_us as f64 / 1000.0;
322        self.tcp_packets = self.tcp_packets.saturating_add(1);
323        let delta = rtt_ms - self.tcp_avg_ms;
324        self.tcp_avg_ms += delta / f64::from(self.tcp_packets);
325        self.tcp_m2 += delta * (rtt_ms - self.tcp_avg_ms);
326    }
327
328    fn message(&self) -> proto::Ping {
329        let tcp = self.tcp_packets > 0;
330        proto::Ping {
331            timestamp: Some(self.now_us()),
332            tcp_packets: tcp.then_some(self.tcp_packets),
333            tcp_ping_avg: tcp.then_some(self.tcp_avg_ms as f32),
334            tcp_ping_var: tcp.then(|| (self.tcp_m2 / f64::from(self.tcp_packets)) as f32),
335            udp_packets: self.udp.map(|u| u.packets),
336            udp_ping_avg: self.udp.map(|u| u.avg_ms),
337            good: Some(self.crypt.good),
338            late: Some(self.crypt.late),
339            lost: Some(self.crypt.lost),
340            resync: Some(self.crypt.resync),
341            ..Default::default()
342        }
343    }
344}
345
346/// Cloneable command API. All commands are queued without waiting; denials arrive later as
347/// [`Event::PermissionDenied`], successes as state updates. Messages the server rate-limits
348/// (own `UserState`, `TextMessage`) are paced to a burst of 4 and then one per 1.2 s, so batch
349/// related changes (e.g. `listen` takes several channels) instead of sending many commands.
350#[derive(Clone)]
351pub struct Handle {
352    tx: mpsc::UnboundedSender<Outgoing>,
353    shared: Arc<Shared>,
354}
355
356/// Connects, logs in and returns once the server has sent `ServerSync`.
357pub async fn connect(config: ConnectConfig) -> Result<Connection, ConnectError> {
358    connect_with_progress(config, None).await
359}
360
361/// Called with the number of channels received so far while the initial tree streams in.
362pub type SyncProgress = Arc<dyn Fn(usize) + Send + Sync>;
363
364/// Like [`connect`], and reports the channel count while waiting for `ServerSync`.
365pub async fn connect_with_progress(
366    config: ConnectConfig,
367    progress: Option<SyncProgress>,
368) -> Result<Connection, ConnectError> {
369    connect_with_tap(config, progress, None).await
370}
371
372/// Like [`connect_with_progress`], and hands every frame from the start of the login on to
373/// `tap`, for debugging tools.
374pub async fn connect_with_tap(
375    config: ConnectConfig,
376    progress: Option<SyncProgress>,
377    tap: Option<Arc<dyn PacketTap>>,
378) -> Result<Connection, ConnectError> {
379    let limit = config.login_timeout;
380    tokio::time::timeout(limit, connect_inner(config, progress, tap))
381        .await
382        .map_err(|_| ConnectError::Timeout)?
383}
384
385async fn connect_inner(
386    config: ConnectConfig,
387    progress: Option<SyncProgress>,
388    tap: Option<Arc<dyn PacketTap>>,
389) -> Result<Connection, ConnectError> {
390    let server_name = ServerName::try_from(config.host.clone())
391        .map_err(|_| ConnectError::InvalidAddress(config.host.clone()))?;
392    let (tls_config, seen_cert) = tls::client_config(config.identity, config.verify_cert)?;
393    // Resolved separately so a lookup failure can be told apart from an unreachable host.
394    let addrs: Vec<SocketAddr> = tokio::net::lookup_host((config.host.as_str(), config.port))
395        .await
396        .map_err(ConnectError::Resolve)?
397        .collect();
398    if addrs.is_empty() {
399        return Err(ConnectError::Resolve(std::io::Error::new(
400            std::io::ErrorKind::NotFound,
401            "no addresses found",
402        )));
403    }
404    let tcp = TcpStream::connect(addrs.as_slice()).await?;
405    // Small control messages (and tunnelled voice) must not wait for Nagle.
406    tcp.set_nodelay(true)?;
407    let mut stream = match TlsConnector::from(tls_config)
408        .connect(server_name, tcp)
409        .await
410    {
411        Ok(stream) => stream,
412        Err(err) => {
413            let tls_err = err
414                .get_ref()
415                .and_then(|e| e.downcast_ref::<rustls::Error>())
416                .cloned();
417            return Err(match (tls_err, seen_cert.take()) {
418                (
419                    Some(rustls::Error::InvalidCertificate(
420                        rustls::CertificateError::ApplicationVerificationFailure,
421                    )),
422                    Some(cert),
423                ) => ConnectError::CertificateRejected(Box::new(cert)),
424                (Some(e), _) => ConnectError::Tls(e),
425                (None, _) => ConnectError::Io(err),
426            });
427        }
428    };
429    // The verifier keeps the certificate it saw; take it for the server info panel.
430    let server_cert = seen_cert.take();
431    // Version must reach the server before Authenticate: the server judges the client by the
432    // version it knows when handling Authenticate (e.g. the "client does not support
433    // ChannelListener" warning for clients it takes as pre-1.4). Version is a rate-limited type,
434    // so the paced write loop would let the unlimited Authenticate overtake it; both are
435    // therefore written here, in order, before the write loop starts.
436    let mut handshake = BytesMut::new();
437    let tokens = config.tokens.clone();
438    for frame in handshake_frames(config.username, config.password, config.tokens) {
439        frame.encode(&mut handshake);
440    }
441    async {
442        stream.write_all(&handshake).await?;
443        stream.flush().await
444    }
445    .await?;
446    let (mut reader, writer) = tokio::io::split(stream);
447
448    let (tx, rx) = mpsc::unbounded_channel();
449    let (events_tx, events) = mpsc::unbounded_channel();
450    let shared = Arc::new(Shared {
451        state: Mutex::new(ServerState::default()),
452        shutdown: Notify::new(),
453        closing: AtomicBool::new(false),
454        pong: watch::Sender::new(0),
455        reason: Mutex::new(None),
456        connected: AtomicBool::new(true),
457        low_expired: AtomicU64::new(0),
458        tokens: Mutex::new(tokens),
459        ping: Mutex::default(),
460        tap,
461    });
462    tokio::spawn(write_loop(writer, rx, shared.clone()));
463    let handle = Handle {
464        tx,
465        shared: shared.clone(),
466    };
467
468    let mut dispatcher = Dispatcher {
469        shared: shared.clone(),
470        events: events_tx,
471        synced: false,
472    };
473    let mut buf = BytesMut::with_capacity(16 * 1024);
474    let mut reported = 0;
475    loop {
476        if reader
477            .read_buf(&mut buf)
478            .await
479            .inspect_err(|_| handle.close())?
480            == 0
481        {
482            handle.close();
483            return Err(ConnectError::Closed);
484        }
485        while let Some(frame) = framing::decode(&mut buf).inspect_err(|_| handle.close())? {
486            match dispatcher.handle(frame) {
487                Control::Continue => {
488                    if let Some(progress) = &progress {
489                        let count = handle.with_state(|s| s.channels.len());
490                        if count != reported {
491                            reported = count;
492                            progress(count);
493                        }
494                    }
495                }
496                Control::Synced => {
497                    tokio::spawn(read_loop(reader, buf, dispatcher, handle.tx.downgrade()));
498                    return Ok(Connection {
499                        handle,
500                        events,
501                        server_cert,
502                    });
503                }
504                Control::Rejected(reason, message) => {
505                    handle.close();
506                    return Err(ConnectError::Rejected { reason, message });
507                }
508                Control::Kicked(_) => {
509                    handle.close();
510                    return Err(ConnectError::Closed);
511                }
512            }
513        }
514    }
515}
516
517/// The release name sent in the Version message; see [`set_client_release`].
518static CLIENT_RELEASE: OnceLock<String> = OnceLock::new();
519
520/// Sets the release name servers show for this client (default `Mumcord <crate version>`), so an
521/// app can report its own build version. A process-wide setting, because the name belongs to the
522/// running program rather than to one connection; only the first call takes effect.
523pub fn set_client_release(release: impl Into<String>) {
524    // A second call keeps the first name; nothing to report.
525    let _ = CLIENT_RELEASE.set(release.into());
526}
527
528fn client_release() -> String {
529    CLIENT_RELEASE
530        .get()
531        .cloned()
532        .unwrap_or_else(|| format!("Mumcord {}", env!("CARGO_PKG_VERSION")))
533}
534
535/// The login messages in the order the server must receive them: Version, then Authenticate.
536fn handshake_frames(username: String, password: Option<String>, tokens: Vec<String>) -> [Frame; 2] {
537    let version = proto::Version {
538        version_v1: Some(MumbleVersion::CLIENT.to_v1()),
539        version_v2: Some(MumbleVersion::CLIENT.to_v2()),
540        release: Some(client_release()),
541        os: Some(std::env::consts::OS.to_owned()),
542        os_version: None,
543    };
544    let auth = proto::Authenticate {
545        username: Some(username),
546        password,
547        tokens,
548        opus: Some(true),
549        ..Default::default()
550    };
551    [
552        Frame::from_message(MessageType::Version, &version),
553        Frame::from_message(MessageType::Authenticate, &auth),
554    ]
555}
556
557fn access_tokens_message(tokens: Vec<String>) -> proto::Authenticate {
558    proto::Authenticate {
559        tokens,
560        ..Default::default()
561    }
562}
563
564async fn write_loop<W: AsyncWrite + Unpin>(
565    writer: W,
566    mut rx: mpsc::UnboundedReceiver<Outgoing>,
567    shared: Arc<Shared>,
568) {
569    let mut ping =
570        tokio::time::interval_at(tokio::time::Instant::now() + PING_INTERVAL, PING_INTERVAL);
571    let mut pong = shared.pong.subscribe();
572    let mut out = Writer::new(writer, shared.clone(), tokio::time::Instant::now());
573    // Set while a requested disconnect drains the queue: the deadline and the flush that ends it.
574    let mut closing: Option<tokio::time::Instant> = None;
575    let mut drained: Option<oneshot::Receiver<()>> = None;
576    let mut handles_gone = false;
577    let result = loop {
578        let now = tokio::time::Instant::now();
579        let messages_ready_at = out.messages.ready_at(now);
580        let plugin_data_ready_at = out.plugin_data.ready_at(now);
581        let step = tokio::select! {
582            msg = rx.recv(), if !handles_gone => match msg {
583                Some(Outgoing::Frame(frame)) => match ratelimit::limit_of(frame.kind) {
584                    Some(Limit::Messages) => {
585                        out.messages.push(frame, now);
586                        Ok(())
587                    }
588                    Some(Limit::PluginData) => {
589                        out.plugin_data.push(frame, now);
590                        Ok(())
591                    }
592                    None => out.write(&frame, None).await,
593                },
594                Some(Outgoing::LowPluginData(frame, sent)) => {
595                    out.plugin_data.push_low(frame, sent, now);
596                    Ok(())
597                }
598                Some(Outgoing::Flush(done)) => {
599                    out.flushes.push(out.flush_marker(done));
600                    out.confirm_written().await
601                }
602                Some(Outgoing::Disconnect) | None if closing.is_none() => {
603                    handles_gone = msg.is_none();
604                    closing = Some(tokio::time::Instant::now() + DRAIN_TIMEOUT);
605                    let (done, wait) = oneshot::channel();
606                    drained = Some(wait);
607                    out.flushes.push(out.flush_marker(done));
608                    out.confirm_written().await
609                }
610                Some(Outgoing::Disconnect) => Ok(()),
611                None => {
612                    handles_gone = true;
613                    Ok(())
614                }
615                Some(Outgoing::Abort) => break Ok(()),
616            },
617            () = sleep_until_some(messages_ready_at) => match out.messages.pop() {
618                Some((frame, _)) => out.write_paced(&frame, None).await,
619                None => Ok(()),
620            },
621            () = sleep_until_some(plugin_data_ready_at) => {
622                out.expire_low(tokio::time::Instant::now());
623                match out.plugin_data.pop() {
624                    Some((frame, pacing)) => out.write_paced(&frame, Some(pacing)).await,
625                    None => match out.plugin_data.pop_low() {
626                        Some((frame, pacing, sent)) => {
627                            let written = out.write(&frame, Some(pacing)).await;
628                            if written.is_ok() {
629                                // The sender may have stopped waiting.
630                                let _ = sent.send(LowOutcome::Sent);
631                            }
632                            written
633                        }
634                        None => Ok(()),
635                    },
636                }
637            }
638            Ok(()) = pong.changed() => {
639                out.confirm_answered(*pong.borrow_and_update());
640                Ok(())
641            }
642            () = done_or_pending(&mut drained) => break Ok(()),
643            () = sleep_until_some(closing) => {
644                tracing::warn!("disconnect timed out before the queue was sent");
645                break Ok(());
646            }
647            _ = ping.tick() => {
648                let frame = Frame::from_message(MessageType::Ping, &shared.ping().message());
649                out.write(&frame, None).await
650            }
651        };
652        if let Err(err) = step {
653            break Err(err);
654        }
655    };
656    out.log_dropped();
657    match result {
658        Ok(()) => {
659            // Best effort close_notify; the connection is going away either way.
660            let _ = out.writer.shutdown().await;
661            shared.shut_down(DisconnectReason::Requested);
662        }
663        Err(err) => {
664            tracing::warn!(error = %err, "control channel write failed");
665            shared.shut_down(DisconnectReason::Error(err.to_string()));
666        }
667    }
668}
669
670/// The write side of the control channel: rate-limited frames wait here in order, one queue per
671/// server bucket; everything else (voice, pings) goes out at once.
672struct Writer<W> {
673    writer: W,
674    buf: BytesMut,
675    shared: Arc<Shared>,
676    messages: Paced,
677    plugin_data: Paced,
678    /// Flushes whose frames are not all written yet.
679    flushes: Vec<FlushMarker>,
680    /// Flushes whose frames are written, waiting for the answer to the ping with this timestamp.
681    unanswered: Vec<(u64, oneshot::Sender<()>)>,
682}
683
684/// A flush waits until both queues have sent as many frames as they had taken when it arrived.
685struct FlushMarker {
686    messages: u64,
687    plugin_data: u64,
688    done: oneshot::Sender<()>,
689}
690
691impl<W: AsyncWrite + Unpin> Writer<W> {
692    fn new(writer: W, shared: Arc<Shared>, now: tokio::time::Instant) -> Self {
693        Self {
694            writer,
695            buf: BytesMut::new(),
696            shared,
697            messages: Paced::new(Limit::Messages, now),
698            plugin_data: Paced::new(Limit::PluginData, now),
699            flushes: Vec::new(),
700            unanswered: Vec::new(),
701        }
702    }
703
704    async fn write(&mut self, frame: &Frame, pacing: Option<TapPacing>) -> std::io::Result<()> {
705        self.buf.clear();
706        frame.encode(&mut self.buf);
707        self.writer.write_all(&self.buf).await?;
708        self.writer.flush().await?;
709        self.shared.tap(TapDirection::Sent, frame, pacing);
710        Ok(())
711    }
712
713    async fn write_paced(
714        &mut self,
715        frame: &Frame,
716        pacing: Option<TapPacing>,
717    ) -> std::io::Result<()> {
718        self.write(frame, pacing).await?;
719        self.confirm_written().await
720    }
721
722    fn flush_marker(&self, done: oneshot::Sender<()>) -> FlushMarker {
723        FlushMarker {
724            messages: self.messages.pushed,
725            plugin_data: self.plugin_data.pushed,
726            done,
727        }
728    }
729
730    /// Sends one ping for all flushes whose frames are now written; its answer completes them.
731    async fn confirm_written(&mut self) -> std::io::Result<()> {
732        let (sent, waiting): (Vec<_>, Vec<_>) = std::mem::take(&mut self.flushes)
733            .into_iter()
734            .partition(|f| {
735                self.messages.popped >= f.messages && self.plugin_data.popped >= f.plugin_data
736            });
737        self.flushes = waiting;
738        if sent.is_empty() {
739            return Ok(());
740        }
741        let ping = self.shared.ping().message();
742        let stamp = ping.timestamp.unwrap_or_default();
743        self.unanswered
744            .extend(sent.into_iter().map(|f| (stamp, f.done)));
745        self.write(&Frame::from_message(MessageType::Ping, &ping), None)
746            .await
747    }
748
749    /// Completes the flushes whose ping the server has answered (`answered` is its timestamp).
750    fn confirm_answered(&mut self, answered: u64) {
751        let (done, waiting): (Vec<_>, Vec<_>) = std::mem::take(&mut self.unanswered)
752            .into_iter()
753            .partition(|(stamp, _)| *stamp <= answered);
754        self.unanswered = waiting;
755        for (_, flush) in done {
756            // The caller may have stopped waiting.
757            let _ = flush.send(());
758        }
759    }
760
761    /// Drops low-priority plugin data that waited too long and counts it.
762    fn expire_low(&mut self, now: tokio::time::Instant) {
763        let expired = self.plugin_data.expire_low(now);
764        if expired > 0 {
765            self.shared
766                .low_expired
767                .fetch_add(expired, Ordering::Relaxed);
768            tracing::debug!(
769                expired,
770                "low-priority plugin data dropped after waiting too long"
771            );
772        }
773    }
774
775    /// Reports what is still queued and will not be sent: message kinds and counts, no content.
776    fn log_dropped(&self) {
777        let mut dropped: BTreeMap<u16, usize> = BTreeMap::new();
778        let low = self.plugin_data.low.iter().map(|l| &l.frame);
779        for frame in self
780            .messages
781            .queue
782            .iter()
783            .chain(&self.plugin_data.queue)
784            .map(|(frame, _)| frame)
785            .chain(low)
786        {
787            *dropped.entry(frame.kind).or_default() += 1;
788        }
789        for (kind, count) in dropped {
790            let kind = MessageType::from_u16(kind);
791            tracing::warn!(?kind, count, "queued messages dropped on disconnect");
792        }
793    }
794}
795
796/// Frames that count against one of the server's rate limits, sent no faster than it allows.
797struct Paced {
798    bucket: LeakyBucket,
799    /// Frames with the time they were queued.
800    queue: VecDeque<(Frame, tokio::time::Instant)>,
801    /// Best-effort frames that go only when `queue` is empty. Flushes do not wait for them.
802    low: VecDeque<LowFrame>,
803    /// Frames taken and sent so far, for flushes to tell when their frames are out.
804    pushed: u64,
805    popped: u64,
806}
807
808impl Paced {
809    fn new(limit: Limit, now: tokio::time::Instant) -> Self {
810        Self {
811            bucket: LeakyBucket::new(limit, now),
812            queue: VecDeque::new(),
813            low: VecDeque::new(),
814            pushed: 0,
815            popped: 0,
816        }
817    }
818
819    fn push(&mut self, frame: Frame, now: tokio::time::Instant) {
820        self.queue.push_back((frame, now));
821        self.pushed += 1;
822    }
823
824    fn push_low(
825        &mut self,
826        frame: Frame,
827        sent: oneshot::Sender<LowOutcome>,
828        now: tokio::time::Instant,
829    ) {
830        self.low.push_back(LowFrame {
831            frame,
832            queued: now,
833            sent,
834        });
835    }
836
837    /// When the next queued frame may go, or `None` with nothing queued. While only core frames
838    /// keep the bucket busy this still wakes per slot, which is also when low frames expire.
839    fn ready_at(&self, now: tokio::time::Instant) -> Option<tokio::time::Instant> {
840        (!self.queue.is_empty() || !self.low.is_empty()).then(|| now + self.bucket.wait_time(now))
841    }
842
843    /// The next frame to send; counted as sent, since a failed write ends the connection anyway.
844    fn pop(&mut self) -> Option<(Frame, TapPacing)> {
845        let (frame, queued) = self.queue.pop_front()?;
846        self.popped += 1;
847        Some((frame, self.take(queued)))
848    }
849
850    /// The next low-priority frame, for when [`Self::pop`] has none.
851    fn pop_low(&mut self) -> Option<(Frame, TapPacing, oneshot::Sender<LowOutcome>)> {
852        let LowFrame {
853            frame,
854            queued,
855            sent,
856        } = self.low.pop_front()?;
857        Some((frame, self.take(queued), sent))
858    }
859
860    fn take(&mut self, queued: tokio::time::Instant) -> TapPacing {
861        let now = tokio::time::Instant::now();
862        self.bucket.take(now);
863        TapPacing {
864            delay: now.saturating_duration_since(queued),
865            tokens_left: self.bucket.tokens_left(now),
866        }
867    }
868
869    /// Drops low frames queued longer than [`LOW_PRIORITY_MAX_WAIT`]; returns how many.
870    fn expire_low(&mut self, now: tokio::time::Instant) -> u64 {
871        let mut expired = 0;
872        // Queued in order, so the oldest are at the front.
873        while self
874            .low
875            .front()
876            .is_some_and(|l| now.saturating_duration_since(l.queued) > LOW_PRIORITY_MAX_WAIT)
877        {
878            if let Some(l) = self.low.pop_front() {
879                // The sender may have stopped waiting.
880                let _ = l.sent.send(LowOutcome::Expired);
881                expired += 1;
882            }
883        }
884        expired
885    }
886}
887
888struct LowFrame {
889    frame: Frame,
890    queued: tokio::time::Instant,
891    sent: oneshot::Sender<LowOutcome>,
892}
893
894/// Waits for `rx` to be answered, or forever when there is nothing to wait for.
895async fn done_or_pending(rx: &mut Option<oneshot::Receiver<()>>) {
896    match rx {
897        Some(rx) => {
898            let _ = rx.await;
899        }
900        None => std::future::pending().await,
901    }
902}
903
904/// Sleeps until `at`, or forever when there is nothing to wait for.
905async fn sleep_until_some(at: Option<tokio::time::Instant>) {
906    match at {
907        Some(at) => tokio::time::sleep_until(at).await,
908        None => std::future::pending().await,
909    }
910}
911
912async fn read_loop(
913    mut reader: ReadHalf<TlsStream<TcpStream>>,
914    mut buf: BytesMut,
915    mut dispatcher: Dispatcher,
916    writer: mpsc::WeakUnboundedSender<Outgoing>,
917) {
918    let shared = dispatcher.shared.clone();
919    let reason = 'outer: loop {
920        // Frames already buffered during login are handled before reading more.
921        loop {
922            match framing::decode(&mut buf) {
923                Ok(Some(frame)) => {
924                    if let Control::Kicked(reason) = dispatcher.handle(frame) {
925                        break 'outer reason;
926                    }
927                }
928                Ok(None) => break,
929                Err(err) => break 'outer DisconnectReason::Error(err.to_string()),
930            }
931        }
932        tokio::select! {
933            read = tokio::time::timeout(READ_TIMEOUT, reader.read_buf(&mut buf)) => match read {
934                Err(_) => break DisconnectReason::Timeout,
935                Ok(Ok(0)) => break DisconnectReason::Closed,
936                Ok(Ok(_)) => {}
937                Ok(Err(err)) => break DisconnectReason::Error(err.to_string()),
938            },
939            () = shared.shutdown.notified() => {
940                let stored = shared.reason.lock().unwrap_or_else(|p| p.into_inner()).take();
941                break stored.unwrap_or(DisconnectReason::Requested);
942            }
943        }
944    };
945    shared.connected.store(false, Ordering::Release);
946    // Stop the writer too; it is already gone if it caused the shutdown.
947    if let Some(tx) = writer.upgrade() {
948        let _ = tx.send(Outgoing::Abort);
949    }
950    tracing::info!(?reason, "control channel closed");
951    let _ = dispatcher.events.send(Event::Disconnected(reason));
952}
953
954enum Control {
955    Continue,
956    Synced,
957    Rejected(RejectReason, String),
958    Kicked(DisconnectReason),
959}
960
961struct Dispatcher {
962    shared: Arc<Shared>,
963    events: mpsc::UnboundedSender<Event>,
964    /// Before `ServerSync` the initial tree is only stored, not announced, so consumers don't
965    /// treat everyone already on the server as "just joined".
966    synced: bool,
967}
968
969fn decode<M: Message + Default>(kind: &'static str, payload: Bytes) -> Result<M, ProtocolError> {
970    M::decode(payload).map_err(|source| ProtocolError::Decode { kind, source })
971}
972
973impl Dispatcher {
974    fn emit(&self, event: Event) {
975        // A dropped receiver only means nobody listens any more.
976        let _ = self.events.send(event);
977    }
978
979    fn emit_synced(&self, event: Event) {
980        if self.synced {
981            self.emit(event);
982        }
983    }
984
985    fn handle(&mut self, frame: Frame) -> Control {
986        self.shared.tap(TapDirection::Received, &frame, None);
987        let Some(kind) = frame.message_type() else {
988            tracing::debug!(kind = frame.kind, "ignoring unknown control message");
989            return Control::Continue;
990        };
991        match self.handle_known(kind, frame.payload) {
992            Ok(control) => control,
993            Err(err) => {
994                // One malformed message is not worth dropping the connection over.
995                tracing::warn!(error = %err, "skipping malformed control message");
996                Control::Continue
997            }
998        }
999    }
1000
1001    fn handle_known(
1002        &mut self,
1003        kind: MessageType,
1004        payload: Bytes,
1005    ) -> Result<Control, ProtocolError> {
1006        let shared = self.shared.clone();
1007        match kind {
1008            MessageType::UdpTunnel => self.emit(Event::UdpTunnel(payload)),
1009            MessageType::Version => {
1010                let m: proto::Version = decode("Version", payload)?;
1011                let mut state = shared.state();
1012                state.server.version = MumbleVersion::from_wire(m.version_v1, m.version_v2);
1013                state.server.release = m.release;
1014                state.server.os = m.os;
1015            }
1016            MessageType::Reject => {
1017                let m: proto::Reject = decode("Reject", payload)?;
1018                let reason = RejectReason::from_wire(m.r#type);
1019                return Ok(Control::Rejected(reason, m.reason.unwrap_or_default()));
1020            }
1021            MessageType::ServerSync => {
1022                let m: proto::ServerSync = decode("ServerSync", payload)?;
1023                shared.state().apply_server_sync(&m);
1024                self.synced = true;
1025                return Ok(Control::Synced);
1026            }
1027            MessageType::ChannelState => {
1028                let m: proto::ChannelState = decode("ChannelState", payload)?;
1029                let channel = shared.state().apply_channel_state(m).cloned();
1030                if let Some(channel) = channel {
1031                    self.emit_synced(Event::ChannelUpdated(channel));
1032                }
1033            }
1034            MessageType::ChannelRemove => {
1035                let m: proto::ChannelRemove = decode("ChannelRemove", payload)?;
1036                let removed = shared.state().apply_channel_remove(m.channel_id);
1037                if let Some(channel) = removed {
1038                    self.emit_synced(Event::ChannelRemoved(channel));
1039                }
1040            }
1041            MessageType::UserState => {
1042                let m: proto::UserState = decode("UserState", payload)?;
1043                let change = shared.state().apply_user_state(m);
1044                if let Some(change) = change {
1045                    self.emit_synced(Event::UserUpdated(change));
1046                }
1047            }
1048            MessageType::UserRemove => {
1049                let m: proto::UserRemove = decode("UserRemove", payload)?;
1050                let ban = m.ban.unwrap_or(false);
1051                let (removed, own) = {
1052                    let mut state = shared.state();
1053                    (
1054                        state.apply_user_remove(m.session),
1055                        state.session == Some(m.session),
1056                    )
1057                };
1058                if own {
1059                    return Ok(Control::Kicked(DisconnectReason::Kicked {
1060                        actor: m.actor,
1061                        reason: m.reason,
1062                        ban,
1063                    }));
1064                }
1065                if let Some(user) = removed {
1066                    self.emit_synced(Event::UserRemoved {
1067                        user,
1068                        actor: m.actor,
1069                        reason: m.reason,
1070                        ban,
1071                    });
1072                }
1073            }
1074            MessageType::TextMessage => {
1075                let m: proto::TextMessage = decode("TextMessage", payload)?;
1076                let sender = m.actor.and_then(|actor| {
1077                    shared.state().users.get(&actor).map(|user| User {
1078                        comment: None,
1079                        texture: None,
1080                        ..user.clone()
1081                    })
1082                });
1083                self.emit(Event::TextMessage(TextMessage {
1084                    actor: m.actor,
1085                    sender,
1086                    target: TextTarget {
1087                        sessions: m.session,
1088                        channels: m.channel_id,
1089                        trees: m.tree_id,
1090                    },
1091                    message: m.message,
1092                }));
1093            }
1094            MessageType::PluginDataTransmission => {
1095                let m: proto::PluginDataTransmission = decode("PluginDataTransmission", payload)?;
1096                self.emit(Event::PluginData(PluginData {
1097                    sender: m.sender_session,
1098                    data_id: m.data_id.unwrap_or_default(),
1099                    data: m.data.map(Bytes::from).unwrap_or_default(),
1100                }));
1101            }
1102            MessageType::UserStats => {
1103                let m: proto::UserStats = decode("UserStats", payload)?;
1104                self.emit(Event::UserStats(Box::new(m)));
1105            }
1106            MessageType::PermissionDenied => {
1107                let m: proto::PermissionDenied = decode("PermissionDenied", payload)?;
1108                self.emit(Event::PermissionDenied(m.into()));
1109            }
1110            MessageType::CryptSetup => {
1111                let m: proto::CryptSetup = decode("CryptSetup", payload)?;
1112                let crypt = CryptSetup::from(m);
1113                {
1114                    let mut state = shared.state();
1115                    // A full setup carries the key; later ones only refresh a nonce.
1116                    match (&mut state.crypt, crypt.key.is_some()) {
1117                        (Some(current), false) => {
1118                            if crypt.server_nonce.is_some() {
1119                                current.server_nonce.clone_from(&crypt.server_nonce);
1120                            }
1121                        }
1122                        (slot, _) => *slot = Some(crypt.clone()),
1123                    }
1124                }
1125                self.emit(Event::CryptSetup(crypt));
1126            }
1127            MessageType::PermissionQuery => {
1128                let m: proto::PermissionQuery = decode("PermissionQuery", payload)?;
1129                shared.state().apply_permission_query(&m);
1130                self.emit_synced(Event::PermissionsUpdated {
1131                    channel_id: m.channel_id,
1132                    flush: m.flush == Some(true),
1133                });
1134            }
1135            MessageType::Acl => {
1136                let m: proto::Acl = decode("Acl", payload)?;
1137                self.emit(Event::Acl(Box::new(m)));
1138            }
1139            MessageType::QueryUsers => {
1140                let m: proto::QueryUsers = decode("QueryUsers", payload)?;
1141                self.emit(Event::UsersQueried(m));
1142            }
1143            MessageType::BanList => {
1144                let m: proto::BanList = decode("BanList", payload)?;
1145                self.emit(Event::BanList(crate::ban::from_message(&m)));
1146            }
1147            MessageType::UserList => {
1148                let m: proto::UserList = decode("UserList", payload)?;
1149                self.emit(Event::UserList(crate::registered::from_message(&m)));
1150            }
1151            MessageType::ServerConfig => {
1152                let m: proto::ServerConfig = decode("ServerConfig", payload)?;
1153                let info = {
1154                    let mut state = shared.state();
1155                    state.apply_server_config(m);
1156                    state.server.clone()
1157                };
1158                self.emit_synced(Event::ServerConfigUpdated(info));
1159            }
1160            MessageType::Ping => {
1161                let m: proto::Ping = decode("Ping", payload)?;
1162                if let Some(sent) = m.timestamp {
1163                    self.shared.ping().on_reply(sent);
1164                    self.shared.pong.send_if_modified(|latest| {
1165                        let newer = sent > *latest;
1166                        if newer {
1167                            *latest = sent;
1168                        }
1169                        newer
1170                    });
1171                }
1172            }
1173            _ => tracing::trace!(?kind, "ignoring control message"),
1174        }
1175        Ok(Control::Continue)
1176    }
1177}
1178
1179impl Handle {
1180    fn send(&self, frame: Frame) -> Result<(), CommandError> {
1181        self.queue(Outgoing::Frame(frame))
1182    }
1183
1184    fn queue(&self, out: Outgoing) -> Result<(), CommandError> {
1185        if !self.is_connected() || self.shared.closing.load(Ordering::Acquire) {
1186            return Err(CommandError::NotConnected);
1187        }
1188        self.tx.send(out).map_err(|_| CommandError::NotConnected)
1189    }
1190
1191    /// Closes at once without sending what is queued (login failed).
1192    fn close(&self) {
1193        let _ = self.tx.send(Outgoing::Abort);
1194    }
1195
1196    fn send_user_state(&self, mut m: proto::UserState) -> Result<(), CommandError> {
1197        m.session = self.shared.state().session;
1198        self.send(Frame::from_message(MessageType::UserState, &m))
1199    }
1200
1201    fn require(&self, feature: &'static str, required: MumbleVersion) -> Result<(), CommandError> {
1202        let server = self.server_version().unwrap_or(MumbleVersion::new(0, 0, 0));
1203        if server >= required {
1204            Ok(())
1205        } else {
1206            Err(CommandError::Unsupported {
1207                feature,
1208                required,
1209                server,
1210            })
1211        }
1212    }
1213
1214    pub fn is_connected(&self) -> bool {
1215        self.shared.connected.load(Ordering::Acquire)
1216    }
1217
1218    /// Snapshot of the current server state.
1219    pub fn state(&self) -> ServerState {
1220        self.shared.state().clone()
1221    }
1222
1223    /// Reads the state without cloning it.
1224    pub fn with_state<R>(&self, f: impl FnOnce(&ServerState) -> R) -> R {
1225        f(&self.shared.state())
1226    }
1227
1228    pub fn server_version(&self) -> Option<MumbleVersion> {
1229        self.shared.state().server.version
1230    }
1231
1232    pub fn join_channel(&self, channel_id: u32) -> Result<(), CommandError> {
1233        self.send_user_state(proto::UserState {
1234            channel_id: Some(channel_id),
1235            ..Default::default()
1236        })
1237    }
1238
1239    /// Moves another person to a channel. The server checks the move permission and answers
1240    /// a refusal with `PermissionDenied`.
1241    pub fn move_user(&self, session: u32, channel_id: u32) -> Result<(), CommandError> {
1242        let m = proto::UserState {
1243            session: Some(session),
1244            channel_id: Some(channel_id),
1245            ..Default::default()
1246        };
1247        self.send(Frame::from_message(MessageType::UserState, &m))
1248    }
1249
1250    pub fn set_self_mute(&self, mute: bool) -> Result<(), CommandError> {
1251        self.send_user_state(proto::UserState {
1252            self_mute: Some(mute),
1253            ..Default::default()
1254        })
1255    }
1256
1257    /// Deafening also mutes (the server enforces this too).
1258    pub fn set_self_deaf(&self, deaf: bool) -> Result<(), CommandError> {
1259        let mut m = proto::UserState {
1260            self_deaf: Some(deaf),
1261            ..Default::default()
1262        };
1263        if deaf {
1264            m.self_mute = Some(true);
1265        }
1266        self.send_user_state(m)
1267    }
1268
1269    /// Starts listening to (following) channels. Needs server 1.4+.
1270    pub fn listen(&self, channels: &[u32]) -> Result<(), CommandError> {
1271        self.require("listening", MumbleVersion::LISTENING)?;
1272        self.send_user_state(proto::UserState {
1273            listening_channel_add: channels.to_vec(),
1274            ..Default::default()
1275        })
1276    }
1277
1278    pub fn unlisten(&self, channels: &[u32]) -> Result<(), CommandError> {
1279        self.require("listening", MumbleVersion::LISTENING)?;
1280        self.send_user_state(proto::UserState {
1281            listening_channel_remove: channels.to_vec(),
1282            ..Default::default()
1283        })
1284    }
1285
1286    /// Stores a listener volume factor on the server (1.5+; 0.0 = silent). Older servers keep no
1287    /// such setting, so callers must also apply the volume locally.
1288    pub fn set_listener_volume(&self, channel_id: u32, volume: f32) -> Result<(), CommandError> {
1289        self.require("listener volume", MumbleVersion::LISTENER_VOLUME)?;
1290        self.send_user_state(proto::UserState {
1291            listening_volume_adjustment: vec![proto::user_state::VolumeAdjustment {
1292                listening_channel: Some(channel_id),
1293                volume_adjustment: Some(volume),
1294            }],
1295            ..Default::default()
1296        })
1297    }
1298
1299    /// Sets the own avatar; an empty vector removes it. Scaling to the server limit is the
1300    /// caller's job.
1301    pub fn set_texture(&self, image: Vec<u8>) -> Result<(), CommandError> {
1302        self.send_user_state(proto::UserState {
1303            texture: Some(image),
1304            ..Default::default()
1305        })
1306    }
1307
1308    /// Announces the game context and player identity for positional audio, like the official
1309    /// client: the server exchanges positions only between users with the same context. Empty
1310    /// values mean "no game".
1311    pub fn set_plugin_context(
1312        &self,
1313        context: Vec<u8>,
1314        identity: String,
1315    ) -> Result<(), CommandError> {
1316        self.send_user_state(proto::UserState {
1317            plugin_context: Some(context),
1318            plugin_identity: Some(identity),
1319            ..Default::default()
1320        })
1321    }
1322
1323    /// Sets the own comment (HTML); an empty string removes it.
1324    pub fn set_comment(&self, comment: String) -> Result<(), CommandError> {
1325        self.send_user_state(proto::UserState {
1326            comment: Some(comment),
1327            ..Default::default()
1328        })
1329    }
1330
1331    /// Registers the own certificate under the current name. Needs a client certificate and the
1332    /// SelfRegister permission; success shows up as a `user_id` on the own user.
1333    pub fn register_self(&self) -> Result<(), CommandError> {
1334        self.send_user_state(proto::UserState {
1335            user_id: Some(0),
1336            ..Default::default()
1337        })
1338    }
1339
1340    /// Sends a text message (Mumble-compatible HTML).
1341    pub fn send_text(&self, target: &TextTarget, message: &str) -> Result<(), CommandError> {
1342        let m = proto::TextMessage {
1343            actor: None,
1344            session: target.sessions.clone(),
1345            channel_id: target.channels.clone(),
1346            tree_id: target.trees.clone(),
1347            message: message.to_owned(),
1348        };
1349        self.send(Frame::from_message(MessageType::TextMessage, &m))
1350    }
1351
1352    /// Sends plugin data to the given sessions (server 1.4+). The server drops data over
1353    /// [`PLUGIN_DATA_MAX`] bytes and rate-limits senders; sends are paced below that limit, so a
1354    /// burst is delayed rather than lost, but keep it small and infrequent.
1355    pub fn send_plugin_data(
1356        &self,
1357        receivers: &[u32],
1358        data_id: &str,
1359        data: Vec<u8>,
1360    ) -> Result<(), CommandError> {
1361        self.require("plugin data", MumbleVersion::PLUGIN_DATA)?;
1362        self.send(self.plugin_data_frame(receivers, data_id, data))
1363    }
1364
1365    /// Like [`Self::send_plugin_data`], but for third-party plugin messages: it shares the same
1366    /// server budget yet goes only when no [`Self::send_plugin_data`] frame is waiting, and is
1367    /// dropped after waiting 10 s. The ticket reports which happened.
1368    pub fn send_plugin_data_low(
1369        &self,
1370        receivers: &[u32],
1371        data_id: &str,
1372        data: Vec<u8>,
1373    ) -> Result<PluginTicket, CommandError> {
1374        self.require("plugin data", MumbleVersion::PLUGIN_DATA)?;
1375        let (tx, rx) = oneshot::channel();
1376        self.queue(Outgoing::LowPluginData(
1377            self.plugin_data_frame(receivers, data_id, data),
1378            tx,
1379        ))?;
1380        Ok(PluginTicket(rx))
1381    }
1382
1383    /// Low-priority plugin data dropped on this connection because it waited too long.
1384    pub fn plugin_data_expired(&self) -> u64 {
1385        self.shared.low_expired.load(Ordering::Relaxed)
1386    }
1387
1388    fn plugin_data_frame(&self, receivers: &[u32], data_id: &str, data: Vec<u8>) -> Frame {
1389        let m = proto::PluginDataTransmission {
1390            sender_session: self.shared.state().session,
1391            receiver_sessions: receivers.to_vec(),
1392            data: Some(data),
1393            data_id: Some(data_id.to_owned()),
1394        };
1395        Frame::from_message(MessageType::PluginDataTransmission, &m)
1396    }
1397
1398    /// Asks for full avatars/comments/descriptions that were announced only by hash.
1399    pub fn request_blobs(
1400        &self,
1401        textures: &[u32],
1402        comments: &[u32],
1403        channel_descriptions: &[u32],
1404    ) -> Result<(), CommandError> {
1405        let m = proto::RequestBlob {
1406            session_texture: textures.to_vec(),
1407            session_comment: comments.to_vec(),
1408            channel_description: channel_descriptions.to_vec(),
1409        };
1410        self.send(Frame::from_message(MessageType::RequestBlob, &m))
1411    }
1412
1413    /// Asks for a person's connection figures; `stats_only` leaves out certificates and client
1414    /// version (the server sends those only to admins and for the own session anyway).
1415    pub fn request_user_stats(&self, session: u32, stats_only: bool) -> Result<(), CommandError> {
1416        let m = proto::UserStats {
1417            session: Some(session),
1418            stats_only: Some(stats_only),
1419            ..Default::default()
1420        };
1421        self.send(Frame::from_message(MessageType::UserStats, &m))
1422    }
1423
1424    /// Sets the voice link figures the next keep-alive pings report: the UDP ping (`None` while
1425    /// voice runs over TCP) and the counts of received voice packets.
1426    pub fn report_voice_link(&self, ping: Option<UdpPing>, crypt: CryptStats) {
1427        let mut report = self.shared.ping();
1428        report.udp = ping;
1429        report.crypt = crypt;
1430    }
1431
1432    /// Replaces the access tokens (channel passwords) of this session. A connected server takes
1433    /// only the tokens from a repeated Authenticate, like official clients send it; it then
1434    /// re-evaluates the ACLs and resends which channels the user may enter.
1435    pub fn set_access_tokens(&self, tokens: Vec<String>) -> Result<(), CommandError> {
1436        self.send(Frame::from_message(
1437            MessageType::Authenticate,
1438            &access_tokens_message(tokens.clone()),
1439        ))?;
1440        *self.shared.tokens.lock().unwrap_or_else(|p| p.into_inner()) = tokens;
1441        Ok(())
1442    }
1443
1444    /// The access tokens (channel passwords) this session last sent to the server.
1445    pub fn access_tokens(&self) -> Vec<String> {
1446        self.shared
1447            .tokens
1448            .lock()
1449            .unwrap_or_else(|p| p.into_inner())
1450            .clone()
1451    }
1452
1453    /// Asks the server for the own permissions in a channel.
1454    pub fn query_permissions(&self, channel_id: u32) -> Result<(), CommandError> {
1455        let m = proto::PermissionQuery {
1456            channel_id: Some(channel_id),
1457            ..Default::default()
1458        };
1459        self.send(Frame::from_message(MessageType::PermissionQuery, &m))
1460    }
1461
1462    /// Asks the server for a channel's groups and ACL entries (the ACL editor and the group lookup,
1463    /// issue #300). The answer arrives as [`Event::Acl`].
1464    ///
1465    /// Standard servers answer the query only for users with the Write permission in that channel or
1466    /// in the root channel, so a refusal arrives as [`Event::PermissionDenied`] with
1467    /// `kind == DenyKind::Permission` and `permission == permissions::WRITE`. The client does not
1468    /// decide this itself, because servers differ in who may read a channel's ACLs.
1469    pub fn query_acl(&self, channel_id: u32) -> Result<(), CommandError> {
1470        let m = proto::Acl {
1471            channel_id,
1472            query: Some(true),
1473            ..Default::default()
1474        };
1475        self.send(Frame::from_message(MessageType::Acl, &m))
1476    }
1477
1478    /// Removes a person from the server (issue #253). `ban` is false for a kick, which the person
1479    /// can come back from; true bans them with the server's default length on the certificate and
1480    /// address the server itself knows, so a chosen duration or a single target needs the ban list
1481    /// instead (see [`Handle::query_ban_list`]). Needs the Kick, or the Ban, permission in the root
1482    /// channel; a refusal arrives as [`Event::PermissionDenied`], the removal as
1483    /// [`Event::UserRemoved`] for our own session too.
1484    pub fn remove_user(
1485        &self,
1486        session: u32,
1487        reason: Option<&str>,
1488        ban: bool,
1489    ) -> Result<(), CommandError> {
1490        let m = proto::UserRemove {
1491            session,
1492            actor: None,
1493            reason: reason.filter(|r| !r.is_empty()).map(|r| r.to_owned()),
1494            ban: Some(ban),
1495        };
1496        self.send(Frame::from_message(MessageType::UserRemove, &m))
1497    }
1498
1499    /// Asks the server for its ban list (issue #254); the answer arrives as [`Event::BanList`].
1500    /// Standard servers answer only users with the Ban permission in the root channel, so a
1501    /// refusal arrives as [`Event::PermissionDenied`].
1502    pub fn query_ban_list(&self) -> Result<(), CommandError> {
1503        self.send(Frame::from_message(
1504            MessageType::BanList,
1505            &crate::ban::query_message(),
1506        ))
1507    }
1508
1509    /// Replaces the server's ban list. The protocol has no single-entry change: the server takes
1510    /// the message as the whole list, so the caller sends back what it read plus its own change
1511    /// (FR-10.66). Needs the Ban permission in the root channel.
1512    pub fn send_ban_list(&self, bans: &[crate::ban::Ban]) -> Result<(), CommandError> {
1513        self.send(Frame::from_message(
1514            MessageType::BanList,
1515            &crate::ban::to_message(bans),
1516        ))
1517    }
1518
1519    /// Asks the server for its registered accounts (issue #256); the answer arrives as
1520    /// [`Event::UserList`], as does every list the server pushes after a change. Standard servers
1521    /// answer only users with the Register permission in the root channel, so a refusal arrives as
1522    /// [`Event::PermissionDenied`].
1523    pub fn query_user_list(&self) -> Result<(), CommandError> {
1524        self.send(Frame::from_message(
1525            MessageType::UserList,
1526            &crate::registered::query_message(),
1527        ))
1528    }
1529
1530    /// Changes one registered account (issue #256): `Some(name)` renames it, `None` removes the
1531    /// registration and leaves the person free to connect as a guest again. Unlike the ban list the
1532    /// server applies this entry on its own, so one change is one message. Needs the Register
1533    /// permission in the root channel; a refusal (a name already taken, for example) arrives as
1534    /// [`Event::PermissionDenied`], the effect as a fresh [`Event::UserList`].
1535    pub fn change_registered_user(
1536        &self,
1537        user_id: u32,
1538        name: Option<&str>,
1539    ) -> Result<(), CommandError> {
1540        self.send(Frame::from_message(
1541            MessageType::UserList,
1542            &crate::registered::change_message(&crate::registered::RegisteredUser {
1543                user_id,
1544                name: name.map(str::to_owned),
1545                ..Default::default()
1546            }),
1547        ))
1548    }
1549
1550    /// Replaces a channel's ACL and groups (admin tooling such as test bots; needs the Write
1551    /// permission, e.g. as SuperUser). Unset groups/ACL entries are removed by the server.
1552    pub fn send_acl(&self, acl: proto::Acl) -> Result<(), CommandError> {
1553        self.send(Frame::from_message(MessageType::Acl, &acl))
1554    }
1555
1556    /// Changes another person's state, e.g. server mute, deafen or suppress (admin tooling such
1557    /// as test bots; needs the MuteDeafen permission, e.g. as SuperUser). `m.session` names the
1558    /// person and is sent as given.
1559    pub fn send_admin_user_state(&self, m: proto::UserState) -> Result<(), CommandError> {
1560        self.send(Frame::from_message(MessageType::UserState, &m))
1561    }
1562
1563    /// Creates or edits a channel (admin tooling such as test bots; needs the matching
1564    /// permissions, e.g. as SuperUser). Without `channel_id` the server creates a new channel.
1565    pub fn send_channel_state(&self, state: proto::ChannelState) -> Result<(), CommandError> {
1566        self.send(Frame::from_message(MessageType::ChannelState, &state))
1567    }
1568
1569    /// Asks the server for the names of registered user ids, e.g. those named in a channel's ACL.
1570    /// The answer arrives as [`Event::UsersQueried`].
1571    pub fn query_users(&self, ids: Vec<u32>) -> Result<(), CommandError> {
1572        let m = proto::QueryUsers {
1573            ids,
1574            names: Vec::new(),
1575        };
1576        self.send(Frame::from_message(MessageType::QueryUsers, &m))
1577    }
1578
1579    /// Deletes a channel with its sub-channels; the server moves the people in them to the parent
1580    /// (needs the Write permission on the channel).
1581    pub fn send_channel_remove(&self, channel_id: u32) -> Result<(), CommandError> {
1582        let m = proto::ChannelRemove { channel_id };
1583        self.send(Frame::from_message(MessageType::ChannelRemove, &m))
1584    }
1585
1586    /// Defines (or replaces) voice target `target.id` (1..=[`VOICE_TARGET_MAX`]) for whisper and
1587    /// shout. Audio sent to that id afterwards reaches its receivers; the server silently leaves
1588    /// out receivers the Whisper permission does not allow.
1589    ///
1590    /// [`VOICE_TARGET_MAX`]: crate::VOICE_TARGET_MAX
1591    pub fn send_voice_target(&self, target: &proto::VoiceTarget) -> Result<(), CommandError> {
1592        self.send(Frame::from_message(MessageType::VoiceTarget, target))
1593    }
1594
1595    /// Voice hook: sends a raw Mumble UDP packet through the TCP tunnel.
1596    pub fn send_tunnel(&self, packet: Bytes) -> Result<(), CommandError> {
1597        self.send(Frame {
1598            kind: MessageType::UdpTunnel as u16,
1599            payload: packet,
1600        })
1601    }
1602
1603    /// Voice hook: sends a `CryptSetup` (empty = request a resync; with `client_nonce` = answer a
1604    /// server resync request).
1605    pub fn send_crypt_setup(&self, crypt: CryptSetup) -> Result<(), CommandError> {
1606        let m = proto::CryptSetup::from(crypt);
1607        self.send(Frame::from_message(MessageType::CryptSetup, &m))
1608    }
1609
1610    /// Waits until everything queued so far has been sent and the server has processed it,
1611    /// which a ping answered afterwards confirms. Rate-limited messages beyond the burst take
1612    /// 1.2 s each, so this can take a while; wrap it in a timeout if needed. Fails with
1613    /// [`CommandError::NotConnected`] if the connection ends first.
1614    pub async fn flush(&self) -> Result<(), CommandError> {
1615        let (done, wait) = oneshot::channel();
1616        self.queue(Outgoing::Flush(done))?;
1617        wait.await.map_err(|_| CommandError::NotConnected)
1618    }
1619
1620    /// Closes the connection gracefully: what is still queued is sent first, at the pace the
1621    /// server allows, and confirmed like [`Handle::flush`]; after 2 s the rest is dropped (and
1622    /// logged by kind and count). New commands fail from now on. An [`Event::Disconnected`] with
1623    /// `Requested` follows; [`Handle::closed`] waits for the end.
1624    pub fn disconnect(&self) {
1625        self.shared.closing.store(true, Ordering::Release);
1626        let _ = self.tx.send(Outgoing::Disconnect);
1627    }
1628
1629    /// Sends a Mumble ping and waits for the server's answer: the round trip, for a one-shot
1630    /// reachability check (the server handles messages in order, so the answer also proves the
1631    /// connection is alive). `None` when the connection ends first.
1632    pub async fn ping(&self) -> Option<Duration> {
1633        let mut pong = self.shared.pong.subscribe();
1634        let sent_us = self.shared.ping().now_us();
1635        let frame = Frame::from_message(MessageType::Ping, &self.shared.ping().message());
1636        self.queue(Outgoing::Frame(frame)).ok()?;
1637        let started = Instant::now();
1638        loop {
1639            if *pong.borrow_and_update() > sent_us {
1640                return Some(started.elapsed());
1641            }
1642            pong.changed().await.ok()?;
1643        }
1644    }
1645
1646    /// Resolves once the connection has stopped sending: after [`Handle::disconnect`] that is
1647    /// when the queue is drained or its time is up, otherwise when the connection is lost.
1648    pub fn closed(&self) -> impl Future<Output = ()> + Send + use<> {
1649        let tx = self.tx.clone();
1650        async move { tx.closed().await }
1651    }
1652}
1653
1654#[cfg(test)]
1655mod tests {
1656    use prost::Message;
1657
1658    use super::*;
1659    use crate::permissions;
1660
1661    #[test]
1662    fn pings_report_the_own_tcp_and_udp_figures() {
1663        let mut report = PingReport {
1664            started: Instant::now() - Duration::from_secs(1),
1665            ..Default::default()
1666        };
1667        assert_eq!(report.message().tcp_ping_avg, None);
1668        let now = report.now_us();
1669        report.on_reply(now - 30_000);
1670        report.on_reply(now - 10_000);
1671        report.udp = Some(UdpPing {
1672            avg_ms: 12.5,
1673            packets: 40,
1674        });
1675        let m = report.message();
1676        assert_eq!(m.tcp_packets, Some(2));
1677        let avg = m.tcp_ping_avg.expect("tcp ping");
1678        assert!((20.0..21.0).contains(&avg), "{avg}");
1679        assert!(m.tcp_ping_var.expect("var") > 90.0);
1680        assert_eq!((m.udp_packets, m.udp_ping_avg), (Some(40), Some(12.5)));
1681        assert_eq!(m.good, Some(0));
1682    }
1683
1684    #[test]
1685    fn send_acl_queues_an_acl_frame() {
1686        let (tx, mut rx) = mpsc::unbounded_channel();
1687        let handle = Handle {
1688            tx,
1689            shared: Arc::new(Shared {
1690                state: Mutex::new(ServerState::default()),
1691                shutdown: Notify::new(),
1692                closing: AtomicBool::new(false),
1693                pong: watch::Sender::new(0),
1694                reason: Mutex::new(None),
1695                connected: AtomicBool::new(true),
1696                ping: Mutex::default(),
1697                tap: None,
1698                low_expired: AtomicU64::new(0),
1699                tokens: Mutex::default(),
1700            }),
1701        };
1702        let acl = proto::Acl {
1703            channel_id: 3,
1704            inherit_acls: Some(true),
1705            ..Default::default()
1706        };
1707        handle.send_acl(acl.clone()).expect("queued");
1708        let Ok(Outgoing::Frame(frame)) = rx.try_recv() else {
1709            panic!("expected a frame");
1710        };
1711        assert_eq!(frame.kind, MessageType::Acl as u16);
1712        assert_eq!(proto::Acl::decode(frame.payload).expect("decode"), acl);
1713    }
1714
1715    fn test_shared(state: ServerState) -> Arc<Shared> {
1716        Arc::new(Shared {
1717            state: Mutex::new(state),
1718            shutdown: Notify::new(),
1719            closing: AtomicBool::new(false),
1720            pong: watch::Sender::new(0),
1721            reason: Mutex::new(None),
1722            connected: AtomicBool::new(true),
1723            ping: Mutex::default(),
1724            tap: None,
1725            low_expired: AtomicU64::new(0),
1726            tokens: Mutex::default(),
1727        })
1728    }
1729
1730    #[test]
1731    fn acl_query_sends_a_query_and_the_answer_becomes_an_event() {
1732        let (tx, mut rx) = mpsc::unbounded_channel();
1733        let shared = test_shared(ServerState::default());
1734        let handle = Handle {
1735            tx: tx.clone(),
1736            shared: shared.clone(),
1737        };
1738        handle.query_acl(2).expect("queued");
1739        let Ok(Outgoing::Frame(frame)) = rx.try_recv() else {
1740            panic!("expected a frame");
1741        };
1742        assert_eq!(frame.kind, MessageType::Acl as u16);
1743        let m = proto::Acl::decode(frame.payload).expect("decode");
1744        assert_eq!((m.channel_id, m.query), (2, Some(true)));
1745
1746        // The answer is passed through; the client does not store or interpret a channel's ACLs.
1747        let (events, mut events_rx) = mpsc::unbounded_channel();
1748        let mut dispatcher = Dispatcher {
1749            shared,
1750            events,
1751            synced: true,
1752        };
1753        let acl = proto::Acl {
1754            channel_id: 2,
1755            inherit_acls: Some(true),
1756            groups: vec![proto::acl::ChanGroup {
1757                name: "editors".into(),
1758                add: vec![7],
1759                ..Default::default()
1760            }],
1761            acls: vec![proto::acl::ChanAcl {
1762                grant: Some(permissions::WRITE),
1763                group: Some("editors".into()),
1764                ..Default::default()
1765            }],
1766            query: Some(false),
1767        };
1768        assert!(matches!(
1769            dispatcher.handle(Frame::from_message(MessageType::Acl, &acl)),
1770            Control::Continue
1771        ));
1772        assert!(rx.try_recv().is_err(), "the query is only sent once");
1773        assert_eq!(events_rx.try_recv(), Ok(Event::Acl(Box::new(acl))));
1774    }
1775
1776    #[test]
1777    fn a_text_message_keeps_its_sender_after_the_sender_left() {
1778        let mut state = ServerState::default();
1779        state.users.insert(
1780            9,
1781            User {
1782                session: 9,
1783                name: "Mallory".into(),
1784                cert_hash: Some("abc".into()),
1785                comment: Some("long comment".into()),
1786                texture: Some(vec![1, 2, 3]),
1787                ..Default::default()
1788            },
1789        );
1790        let (events, mut events_rx) = mpsc::unbounded_channel();
1791        let mut dispatcher = Dispatcher {
1792            shared: test_shared(state),
1793            events,
1794            synced: true,
1795        };
1796        let text = proto::TextMessage {
1797            actor: Some(9),
1798            channel_id: vec![0],
1799            message: "hi".into(),
1800            ..Default::default()
1801        };
1802        dispatcher.handle(Frame::from_message(MessageType::TextMessage, &text));
1803        // The sender leaves before anyone handled the message event.
1804        let remove = proto::UserRemove {
1805            session: 9,
1806            ..Default::default()
1807        };
1808        dispatcher.handle(Frame::from_message(MessageType::UserRemove, &remove));
1809        let Ok(Event::TextMessage(message)) = events_rx.try_recv() else {
1810            panic!("expected the text message first");
1811        };
1812        let sender = message
1813            .sender
1814            .expect("the sender as it was when the message came");
1815        assert_eq!(sender.name, "Mallory");
1816        assert_eq!(sender.cert_hash.as_deref(), Some("abc"));
1817        assert_eq!((sender.comment, sender.texture), (None, None));
1818    }
1819
1820    #[test]
1821    fn plugin_data_needs_a_1_4_server_and_names_the_receivers() {
1822        let (tx, mut rx) = mpsc::unbounded_channel();
1823        let state = ServerState {
1824            session: Some(7),
1825            ..Default::default()
1826        };
1827        let handle = Handle {
1828            tx,
1829            shared: Arc::new(Shared {
1830                state: Mutex::new(state),
1831                shutdown: Notify::new(),
1832                closing: AtomicBool::new(false),
1833                pong: watch::Sender::new(0),
1834                reason: Mutex::new(None),
1835                connected: AtomicBool::new(true),
1836                ping: Mutex::default(),
1837                tap: None,
1838                low_expired: AtomicU64::new(0),
1839                tokens: Mutex::default(),
1840            }),
1841        };
1842        assert!(matches!(
1843            handle.send_plugin_data(&[3], "id", vec![1]),
1844            Err(CommandError::Unsupported { .. })
1845        ));
1846        handle.shared.state().server.version = Some(MumbleVersion::new(1, 4, 0));
1847        handle
1848            .send_plugin_data(&[3, 4], "mumcord.test", b"{}".to_vec())
1849            .expect("queued");
1850        let Ok(Outgoing::Frame(frame)) = rx.try_recv() else {
1851            panic!("expected a frame");
1852        };
1853        assert_eq!(frame.kind, MessageType::PluginDataTransmission as u16);
1854        let m = proto::PluginDataTransmission::decode(frame.payload).expect("decode");
1855        assert_eq!(m.sender_session, Some(7));
1856        assert_eq!(m.receiver_sessions, vec![3, 4]);
1857        assert_eq!(m.data_id.as_deref(), Some("mumcord.test"));
1858        assert_eq!(m.data.as_deref(), Some(&b"{}"[..]));
1859    }
1860}
1861
1862#[cfg(test)]
1863mod drain_tests {
1864    use tokio::io::{AsyncReadExt, DuplexStream};
1865    use tokio::task::JoinHandle;
1866
1867    use super::*;
1868
1869    fn shared() -> Arc<Shared> {
1870        Arc::new(Shared {
1871            state: Mutex::new(ServerState::default()),
1872            shutdown: Notify::new(),
1873            closing: AtomicBool::new(false),
1874            pong: watch::Sender::new(0),
1875            reason: Mutex::new(None),
1876            connected: AtomicBool::new(true),
1877            ping: Mutex::default(),
1878            tap: None,
1879            low_expired: AtomicU64::new(0),
1880            tokens: Mutex::default(),
1881        })
1882    }
1883
1884    fn text(i: u32) -> Frame {
1885        let m = proto::TextMessage {
1886            message: format!("message {i}"),
1887            ..Default::default()
1888        };
1889        Frame::from_message(MessageType::TextMessage, &m)
1890    }
1891
1892    /// Plays the server: collects the text messages and plugin data ids until the client closes,
1893    /// answering pings only when `answer` is set.
1894    fn server(
1895        mut stream: DuplexStream,
1896        shared: Arc<Shared>,
1897        answer: bool,
1898    ) -> JoinHandle<Vec<String>> {
1899        tokio::spawn(async move {
1900            let mut buf = BytesMut::new();
1901            let mut texts = Vec::new();
1902            while stream.read_buf(&mut buf).await.expect("read") > 0 {
1903                while let Some(frame) = framing::decode(&mut buf).expect("frame") {
1904                    if frame.kind == MessageType::TextMessage as u16 {
1905                        let m = proto::TextMessage::decode(frame.payload).expect("text");
1906                        texts.push(m.message);
1907                    } else if frame.kind == MessageType::PluginDataTransmission as u16 {
1908                        let m = proto::PluginDataTransmission::decode(frame.payload)
1909                            .expect("plugin data");
1910                        texts.push(m.data_id.expect("data id"));
1911                    } else if frame.kind == MessageType::Ping as u16 && answer {
1912                        let m = proto::Ping::decode(frame.payload).expect("ping");
1913                        shared.pong.send_replace(m.timestamp.expect("timestamp"));
1914                    }
1915                }
1916            }
1917            texts
1918        })
1919    }
1920
1921    fn start(answer: bool) -> (Handle, JoinHandle<Vec<String>>) {
1922        start_with(answer, shared())
1923    }
1924
1925    fn start_with(answer: bool, shared: Arc<Shared>) -> (Handle, JoinHandle<Vec<String>>) {
1926        let (client, server_side) = tokio::io::duplex(64 * 1024);
1927        let (tx, rx) = mpsc::unbounded_channel();
1928        tokio::spawn(write_loop(client, rx, shared.clone()));
1929        let server = server(server_side, shared.clone(), answer);
1930        (Handle { tx, shared }, server)
1931    }
1932
1933    fn expected(count: u32) -> Vec<String> {
1934        (0..count).map(|i| format!("message {i}")).collect()
1935    }
1936
1937    #[tokio::test(start_paused = true)]
1938    async fn disconnect_sends_the_queue_beyond_the_burst_first() {
1939        let (handle, server) = start(true);
1940        let started = tokio::time::Instant::now();
1941        for i in 0..5 {
1942            handle.send(text(i)).expect("queued");
1943        }
1944        handle.disconnect();
1945        assert_eq!(handle.send(text(9)), Err(CommandError::NotConnected));
1946        handle.closed().await;
1947        assert_eq!(server.await.expect("server"), expected(5));
1948        // The fifth waited for the pace: the drain respects the rate limit.
1949        assert!(started.elapsed() >= Duration::from_millis(1200));
1950        assert_eq!(
1951            *handle.shared.reason.lock().expect("reason"),
1952            Some(DisconnectReason::Requested)
1953        );
1954    }
1955
1956    #[tokio::test(start_paused = true)]
1957    async fn disconnect_drops_what_does_not_fit_in_the_timeout() {
1958        let (handle, server) = start(true);
1959        let started = tokio::time::Instant::now();
1960        for i in 0..8 {
1961            handle.send(text(i)).expect("queued");
1962        }
1963        handle.disconnect();
1964        handle.closed().await;
1965        // Burst of 4, one more after 1.2 s; the next would be due at 2.4 s.
1966        assert_eq!(server.await.expect("server"), expected(5));
1967        assert_eq!(started.elapsed(), DRAIN_TIMEOUT);
1968    }
1969
1970    #[tokio::test(start_paused = true)]
1971    async fn disconnect_waits_for_the_server_to_confirm() {
1972        let (handle, server) = start(false);
1973        let started = tokio::time::Instant::now();
1974        handle.send(text(0)).expect("queued");
1975        handle.disconnect();
1976        handle.closed().await;
1977        assert_eq!(server.await.expect("server"), expected(1));
1978        assert_eq!(started.elapsed(), DRAIN_TIMEOUT);
1979    }
1980
1981    #[tokio::test(start_paused = true)]
1982    async fn ping_returns_the_round_trip_of_the_answer() {
1983        let (handle, server) = start(true);
1984        assert!(handle.ping().await.is_some());
1985        // A ping is queued like any other frame; the fake server sees it.
1986        handle.disconnect();
1987        assert_eq!(server.await.expect("server"), Vec::<String>::new());
1988    }
1989
1990    #[tokio::test(start_paused = true)]
1991    async fn ping_gives_up_when_the_server_never_answers() {
1992        let (handle, _server) = start(false);
1993        let waited = tokio::time::timeout(Duration::from_secs(1), handle.ping()).await;
1994        assert!(waited.is_err(), "no answer means no result");
1995    }
1996
1997    #[tokio::test(start_paused = true)]
1998    async fn ping_gives_up_when_the_connection_ends() {
1999        let (handle, server) = start(true);
2000        drop(server);
2001        let _ = handle.ping().await;
2002    }
2003
2004    #[tokio::test(start_paused = true)]
2005    async fn flush_completes_once_the_queue_is_sent_and_confirmed() {
2006        let (handle, server) = start(true);
2007        let started = tokio::time::Instant::now();
2008        for i in 0..6 {
2009            handle.send(text(i)).expect("queued");
2010        }
2011        handle.flush().await.expect("flushed");
2012        // Burst of 4, then 1.2 s each.
2013        assert_eq!(started.elapsed(), Duration::from_millis(2400));
2014        handle.disconnect();
2015        assert_eq!(server.await.expect("server"), expected(6));
2016    }
2017
2018    #[derive(Default)]
2019    struct Recorder(Mutex<Vec<u16>>);
2020
2021    impl PacketTap for Recorder {
2022        fn enabled(&self) -> bool {
2023            true
2024        }
2025
2026        fn on_frame(&self, frame: TappedFrame) {
2027            assert_eq!(frame.direction, TapDirection::Sent);
2028            self.0.lock().expect("recorder").push(frame.kind);
2029        }
2030    }
2031
2032    #[tokio::test(start_paused = true)]
2033    async fn frames_sent_while_draining_reach_the_tap() {
2034        let recorder = Arc::new(Recorder::default());
2035        let mut shared = shared();
2036        Arc::get_mut(&mut shared).expect("unshared").tap = Some(recorder.clone());
2037        let (handle, server) = start_with(true, shared);
2038        for i in 0..6 {
2039            handle.send(text(i)).expect("queued");
2040        }
2041        handle.disconnect();
2042        handle.closed().await;
2043        assert_eq!(server.await.expect("server"), expected(5));
2044        let texts = recorder
2045            .0
2046            .lock()
2047            .expect("recorder")
2048            .iter()
2049            .filter(|kind| **kind == MessageType::TextMessage as u16)
2050            .count();
2051        assert_eq!(texts, 5);
2052    }
2053
2054    fn start_plugin_data() -> (Handle, JoinHandle<Vec<String>>) {
2055        let shared = shared();
2056        shared.state().server.version = Some(MumbleVersion::new(1, 4, 0));
2057        start_with(true, shared)
2058    }
2059
2060    fn send_core(handle: &Handle, ids: std::ops::Range<u32>) {
2061        for i in ids {
2062            handle
2063                .send_plugin_data(&[2], &format!("core {i}"), vec![0])
2064                .expect("queued");
2065        }
2066    }
2067
2068    fn send_low(handle: &Handle, i: u32) -> PluginTicket {
2069        handle
2070            .send_plugin_data_low(&[2], &format!("low {i}"), vec![0])
2071            .expect("queued")
2072    }
2073
2074    #[tokio::test(start_paused = true)]
2075    async fn core_plugin_data_overtakes_low_priority_data() {
2076        let (handle, server) = start_plugin_data();
2077        // The first 12 use up the burst, so everything after waits in the queues.
2078        send_core(&handle, 0..12);
2079        let tickets: Vec<_> = (0..3).map(|i| send_low(&handle, i)).collect();
2080        send_core(&handle, 12..15);
2081        for ticket in tickets {
2082            assert_eq!(ticket.outcome().await, LowOutcome::Sent);
2083        }
2084        handle.disconnect();
2085        let mut want: Vec<_> = (0..15).map(|i| format!("core {i}")).collect();
2086        want.extend((0..3).map(|i| format!("low {i}")));
2087        assert_eq!(server.await.expect("server"), want);
2088        assert_eq!(handle.plugin_data_expired(), 0);
2089    }
2090
2091    #[tokio::test(start_paused = true)]
2092    async fn low_priority_data_goes_at_once_when_nothing_else_waits() {
2093        let (handle, server) = start_plugin_data();
2094        let started = tokio::time::Instant::now();
2095        assert_eq!(send_low(&handle, 0).outcome().await, LowOutcome::Sent);
2096        assert_eq!(started.elapsed(), Duration::ZERO);
2097        handle.disconnect();
2098        assert_eq!(server.await.expect("server"), ["low 0"]);
2099    }
2100
2101    #[tokio::test(start_paused = true)]
2102    async fn low_priority_data_expires_after_ten_seconds_behind_core_data() {
2103        let (handle, server) = start_plugin_data();
2104        let started = tokio::time::Instant::now();
2105        // 12 at once, then one per 300 ms: the queue stays busy for 12 s.
2106        send_core(&handle, 0..52);
2107        let ticket = send_low(&handle, 0);
2108        assert_eq!(ticket.outcome().await, LowOutcome::Expired);
2109        let waited = started.elapsed();
2110        assert!(waited > LOW_PRIORITY_MAX_WAIT, "{waited:?}");
2111        assert!(
2112            waited <= LOW_PRIORITY_MAX_WAIT + Duration::from_millis(300),
2113            "{waited:?}"
2114        );
2115        assert_eq!(handle.plugin_data_expired(), 1);
2116        handle.close();
2117        let sent = server.await.expect("server");
2118        assert!(!sent.iter().any(|id| id.starts_with("low")), "{sent:?}");
2119    }
2120
2121    #[tokio::test(start_paused = true)]
2122    async fn queued_low_priority_data_is_reported_when_the_connection_ends() {
2123        let (handle, server) = start_plugin_data();
2124        send_core(&handle, 0..12);
2125        let ticket = send_low(&handle, 0);
2126        handle.close();
2127        assert_eq!(ticket.outcome().await, LowOutcome::Disconnected);
2128        let sent = server.await.expect("server");
2129        assert!(!sent.iter().any(|id| id.starts_with("low")), "{sent:?}");
2130        assert_eq!(handle.plugin_data_expired(), 0);
2131    }
2132
2133    #[test]
2134    fn low_priority_plugin_data_needs_server_support() {
2135        let (tx, _rx) = mpsc::unbounded_channel();
2136        let handle = Handle {
2137            tx,
2138            shared: shared(),
2139        };
2140        assert!(matches!(
2141            handle.send_plugin_data_low(&[3], "id", vec![1]),
2142            Err(CommandError::Unsupported { .. })
2143        ));
2144    }
2145
2146    #[tokio::test(start_paused = true)]
2147    async fn abort_drops_the_queue() {
2148        let (handle, server) = start(true);
2149        for i in 0..6 {
2150            handle.send(text(i)).expect("queued");
2151        }
2152        handle.close();
2153        handle.closed().await;
2154        // Some of the burst may have gone out already; the paced rest never does.
2155        let sent = server.await.expect("server");
2156        assert!(sent.len() <= 4, "{sent:?}");
2157    }
2158}
2159
2160#[cfg(test)]
2161mod handshake_tests {
2162    use super::*;
2163
2164    #[test]
2165    fn version_precedes_authenticate_with_both_encodings() {
2166        let [version, auth] = handshake_frames("alice".to_owned(), None, vec!["secret".into()]);
2167        assert_eq!(version.message_type(), Some(MessageType::Version));
2168        assert_eq!(auth.message_type(), Some(MessageType::Authenticate));
2169
2170        let v = proto::Version::decode(version.payload).expect("version");
2171        // Server 1.5 reads version_v2 (major << 48 | minor << 32 | patch << 16), older ones v1.
2172        assert_eq!(v.version_v2, Some(0x0001_0005_0000_0000));
2173        assert_eq!(v.version_v1, Some(0x0001_0500));
2174        assert!(
2175            MumbleVersion::from_wire(v.version_v1, v.version_v2)
2176                .is_some_and(|v| v.supports_listening())
2177        );
2178        assert!(v.release.is_some_and(|r| r.starts_with("Mumcord ")));
2179
2180        let a = proto::Authenticate::decode(auth.payload).expect("authenticate");
2181        assert_eq!(a.username.as_deref(), Some("alice"));
2182        assert_eq!(a.opus, Some(true));
2183        assert_eq!(a.tokens, ["secret"]);
2184    }
2185
2186    #[test]
2187    fn the_app_sets_the_release_name_servers_show() {
2188        // Process-wide: keeps the "Mumcord " prefix the other handshake test checks for.
2189        set_client_release("Mumcord dev~a1b2c3d");
2190        set_client_release("Mumcord ignored");
2191        let [version, _] = handshake_frames("alice".to_owned(), None, Vec::new());
2192        let v = proto::Version::decode(version.payload).expect("version");
2193        assert_eq!(v.release.as_deref(), Some("Mumcord dev~a1b2c3d"));
2194    }
2195
2196    #[test]
2197    fn the_handle_remembers_the_tokens_it_sent() {
2198        let (tx, _rx) = mpsc::unbounded_channel();
2199        let handle = Handle {
2200            tx,
2201            shared: Arc::new(Shared {
2202                state: Mutex::new(ServerState::default()),
2203                shutdown: Notify::new(),
2204                closing: AtomicBool::new(false),
2205                pong: watch::Sender::new(0),
2206                reason: Mutex::new(None),
2207                connected: AtomicBool::new(true),
2208                ping: Mutex::default(),
2209                tap: None,
2210                low_expired: AtomicU64::new(0),
2211                tokens: Mutex::new(vec!["login".into()]),
2212            }),
2213        };
2214        assert_eq!(handle.access_tokens(), ["login"]);
2215        handle
2216            .set_access_tokens(vec!["one".into(), "two".into()])
2217            .expect("queued");
2218        assert_eq!(handle.access_tokens(), ["one", "two"]);
2219    }
2220
2221    #[test]
2222    fn access_tokens_resend_carries_only_the_tokens() {
2223        let frame = Frame::from_message(
2224            MessageType::Authenticate,
2225            &access_tokens_message(vec!["one".into(), "two".into()]),
2226        );
2227        let a = proto::Authenticate::decode(frame.payload).expect("authenticate");
2228        assert_eq!(a.tokens, ["one", "two"]);
2229        assert_eq!(a.username, None);
2230        assert_eq!(a.password, None);
2231    }
2232}