feat: audio playback, device selector, i18n fixes

Audio:
- Add AudioPlayback with channel-based cpal output (avoids Send issue)
- Add AudioCapture stub with input device enumeration
- Audio gated behind 'audio' feature flag (needs ALSA/cmake)
- list_output_devices() / list_input_devices() for device selection
- StreamItem::Audio packets forwarded via mpsc channel to cpal callback

i18n:
- Replace all Chinese error messages with English in tsdb
- Replace all Chinese comments/doc strings with English in tscore
- All user-facing strings now in English

Build:
- Add Containerfile.build for containerized builds with audio deps
- CMAKE_POLICY_VERSION_MINIMUM=3.5 workaround for audiopus_sys
- tsclientlib audio feature enabled in workspace (needs cmake)
- 69 tests passing, clippy clean on both audio and non-audio builds
This commit is contained in:
ReTeamSpeak
2026-05-13 00:53:08 +09:00
parent 0d805269b5
commit 073095b06a
27 changed files with 483 additions and 222 deletions
+14
View File
@@ -0,0 +1,14 @@
FROM registry.fedoraproject.org/fedora:44
RUN dnf install -y \
gcc \
pkg-config \
cmake \
openssl-devel \
alsa-lib-devel \
&& dnf clean all
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
WORKDIR /build
+1 -1
View File
@@ -56,5 +56,5 @@ tscore = { path = "tscore" }
tsaudio = { path = "tsaudio" } tsaudio = { path = "tsaudio" }
tsdb = { path = "tsdb" } tsdb = { path = "tsdb" }
shared = { path = "shared" } shared = { path = "shared" }
tsclientlib = { git = "https://github.com/ReSpeak/tsclientlib.git", branch = "master", default-features = false, features = ["default-tls"] } tsclientlib = { git = "https://github.com/ReSpeak/tsclientlib.git", branch = "master", default-features = false, features = ["default-tls", "audio"] }
tsproto-packets = { git = "https://github.com/ReSpeak/tsclientlib.git", branch = "master" } tsproto-packets = { git = "https://github.com/ReSpeak/tsclientlib.git", branch = "master" }
+201
View File
@@ -0,0 +1,201 @@
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use tsclientlib::audio::AudioHandler;
use tsclientlib::ClientId;
use tsproto_packets::packets::{AudioData, InAudioBuf};
const SAMPLE_RATE: u32 = 48000;
const CHANNELS: u16 = 2;
type PacketSender = std::sync::mpsc::Sender<InAudioBuf>;
struct PlaybackState {
sender: PacketSender,
output_device: String,
_stream: cpal::Stream,
}
// cpal::Stream is safe to send across threads (it's a handle to the audio device)
unsafe impl Send for PlaybackState {}
unsafe impl Sync for PlaybackState {}
pub struct AudioPlayback {
state: Option<PlaybackState>,
}
impl AudioPlayback {
pub fn new() -> Self {
Self { state: None }
}
pub fn list_output_devices() -> Vec<String> {
let host = cpal::default_host();
host.output_devices()
.map(|d| d.filter_map(|d| d.name().ok()).collect())
.unwrap_or_default()
}
pub fn list_input_devices() -> Vec<String> {
let host = cpal::default_host();
host.input_devices()
.map(|d| d.filter_map(|d| d.name().ok()).collect())
.unwrap_or_default()
}
pub fn start(&mut self, device_name: Option<&str>) -> Result<(), String> {
let host = cpal::default_host();
let device = if let Some(name) = device_name {
host.output_devices()
.map_err(|e| format!("Failed to enumerate devices: {e}"))?
.find(|d| d.name().map(|n| n == name).unwrap_or(false))
.ok_or_else(|| format!("Output device '{}' not found", name))?
} else {
host.default_output_device()
.ok_or_else(|| "No audio output device found".to_string())?
};
let device_name_str = device.name().unwrap_or_default();
tracing::info!("Using output device: {}", device_name_str);
let config = cpal::StreamConfig {
channels: CHANNELS,
sample_rate: cpal::SampleRate(SAMPLE_RATE),
buffer_size: cpal::BufferSize::Default,
};
let (tx, rx) = std::sync::mpsc::channel::<InAudioBuf>();
let mut handler = AudioHandler::<ClientId>::new();
let stream = device
.build_output_stream(
&config,
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
while let Ok(packet) = rx.try_recv() {
let from = match packet.data().data() {
AudioData::S2C { from, .. } => *from,
AudioData::S2CWhisper { from, .. } => *from,
_ => continue,
};
let _ = handler.handle_packet(ClientId(from), packet);
}
for sample in data.iter_mut() {
*sample = 0.0;
}
handler.fill_buffer(data);
},
|err| tracing::error!("Audio output error: {err}"),
None,
)
.map_err(|e| format!("Failed to build output stream: {e}"))?;
stream
.play()
.map_err(|e| format!("Failed to start playback: {e}"))?;
self.state = Some(PlaybackState {
sender: tx,
output_device: device_name_str,
_stream: stream,
});
tracing::info!("Audio playback started");
Ok(())
}
pub fn stop(&mut self) {
self.state = None;
tracing::info!("Audio playback stopped");
}
pub fn send_packet(&self, packet: InAudioBuf) {
if let Some(ref state) = self.state {
let _ = state.sender.send(packet);
}
}
pub fn output_device(&self) -> &str {
self.state
.as_ref()
.map(|s| s.output_device.as_str())
.unwrap_or("")
}
pub fn is_active(&self) -> bool {
self.state.is_some()
}
}
pub struct AudioCapture {
_stream: Option<cpal::Stream>,
input_device: String,
}
impl AudioCapture {
pub fn new() -> Self {
Self {
_stream: None,
input_device: String::new(),
}
}
pub fn start(
&mut self,
device_name: Option<&str>,
audio_tx: tokio::sync::mpsc::Sender<Vec<f32>>,
) -> Result<(), String> {
let host = cpal::default_host();
let device = if let Some(name) = device_name {
host.input_devices()
.map_err(|e| format!("Failed to enumerate devices: {e}"))?
.find(|d| d.name().map(|n| n == name).unwrap_or(false))
.ok_or_else(|| format!("Input device '{}' not found", name))?
} else {
host.default_input_device()
.ok_or_else(|| "No audio input device found".to_string())?
};
let device_name_str = device.name().unwrap_or_default();
tracing::info!("Using input device: {}", device_name_str);
let config = cpal::StreamConfig {
channels: 1,
sample_rate: cpal::SampleRate(SAMPLE_RATE),
buffer_size: cpal::BufferSize::Default,
};
let stream = device
.build_input_stream(
&config,
move |data: &[f32], _: &cpal::InputCallbackInfo| {
let _ = audio_tx.blocking_send(data.to_vec());
},
|err| tracing::error!("Audio input error: {err}"),
None,
)
.map_err(|e| format!("Failed to build input stream: {e}"))?;
stream
.play()
.map_err(|e| format!("Failed to start capture: {e}"))?;
self._stream = Some(stream);
self.input_device = device_name_str;
tracing::info!("Audio capture started");
Ok(())
}
pub fn stop(&mut self) {
self._stream = None;
tracing::info!("Audio capture stopped");
}
pub fn input_device(&self) -> &str {
&self.input_device
}
pub fn is_active(&self) -> bool {
self._stream.is_some()
}
}
+69
View File
@@ -11,6 +11,9 @@ use tsclientlib::{ChannelId, ClientId, Connection, DisconnectOptions, MessageTar
use tsclientlib::events::{Event, PropertyId}; use tsclientlib::events::{Event, PropertyId};
use tsclientlib::prelude::*; use tsclientlib::prelude::*;
#[cfg(feature = "audio")]
mod audio;
fn main() -> iced::Result { fn main() -> iced::Result {
iced::application("ReTeamSpeak", App::update, App::view) iced::application("ReTeamSpeak", App::update, App::view)
.subscription(App::subscription) .subscription(App::subscription)
@@ -116,6 +119,8 @@ struct App {
error: Option<String>, error: Option<String>,
identity_level: u8, identity_level: u8,
session_id: u64, session_id: u64,
#[cfg(feature = "audio")]
audio: Arc<Mutex<audio::AudioPlayback>>,
} }
impl App { impl App {
@@ -159,6 +164,8 @@ impl App {
error: None, error: None,
identity_level: 0, identity_level: 0,
session_id: 0, session_id: 0,
#[cfg(feature = "audio")]
audio: Arc::new(Mutex::new(audio::AudioPlayback::new())),
}; };
(app, Task::none()) (app, Task::none())
@@ -206,6 +213,8 @@ impl App {
}; };
let handle_store = self.handle.clone(); let handle_store = self.handle.clone();
let event_rx_store = self.event_rx.clone(); let event_rx_store = self.event_rx.clone();
#[cfg(feature = "audio")]
let audio = self.audio.clone();
self.error = None; self.error = None;
self.connected = false; self.connected = false;
@@ -231,6 +240,9 @@ impl App {
*handle_store.lock().await = Some(handle.clone()); *handle_store.lock().await = Some(handle.clone());
#[cfg(feature = "audio")]
tokio::spawn(run_connection(sync_con, event_tx, audio));
#[cfg(not(feature = "audio"))]
tokio::spawn(run_connection(sync_con, event_tx)); tokio::spawn(run_connection(sync_con, event_tx));
handle handle
@@ -270,6 +282,16 @@ impl App {
Message::TsEvent(event) => match event { Message::TsEvent(event) => match event {
TsEvent::Connected => { TsEvent::Connected => {
self.connected = true; self.connected = true;
#[cfg(feature = "audio")]
{
let audio = self.audio.clone();
tokio::spawn(async move {
let mut playback = audio.lock().await;
if let Err(e) = playback.start(None) {
tracing::warn!("Audio playback failed to start: {e}");
}
});
}
Task::none() Task::none()
} }
TsEvent::BookEvents(events) => { TsEvent::BookEvents(events) => {
@@ -293,6 +315,13 @@ impl App {
Task::none() Task::none()
} }
TsEvent::Disconnected => { TsEvent::Disconnected => {
#[cfg(feature = "audio")]
{
let audio = self.audio.clone();
tokio::spawn(async move {
audio.lock().await.stop();
});
}
self.connected = false; self.connected = false;
self.server_name.clear(); self.server_name.clear();
self.channels.clear(); self.channels.clear();
@@ -302,6 +331,13 @@ impl App {
Task::none() Task::none()
} }
TsEvent::Error(e) => { TsEvent::Error(e) => {
#[cfg(feature = "audio")]
{
let audio = self.audio.clone();
tokio::spawn(async move {
audio.lock().await.stop();
});
}
self.error = Some(e); self.error = Some(e);
Task::none() Task::none()
} }
@@ -903,6 +939,38 @@ impl App {
} }
} }
#[cfg(feature = "audio")]
async fn run_connection(con: SyncConnection, event_tx: mpsc::Sender<TsEvent>, audio: Arc<Mutex<audio::AudioPlayback>>) {
let mut stream = con;
while let Some(item) = stream.next().await {
let ts_event = match item {
Ok(SyncStreamItem::BookEvents(events)) => TsEvent::BookEvents(events),
Ok(SyncStreamItem::MessageEvent(msg)) => TsEvent::MessageEvent(msg),
Ok(SyncStreamItem::AudioChange(change)) => match change {
tsclientlib::AudioEvent::CanSendAudio(can) => TsEvent::AudioChange(can, true),
tsclientlib::AudioEvent::CanReceiveAudio(can) => TsEvent::AudioChange(false, can),
},
Ok(SyncStreamItem::IdentityLevelIncreasing(level)) => {
TsEvent::IdentityLevelIncreasing(level)
}
Ok(SyncStreamItem::IdentityLevelIncreased) => TsEvent::IdentityLevelIncreased,
Ok(SyncStreamItem::DisconnectedTemporarily(_)) => TsEvent::DisconnectedTemporarily,
Ok(SyncStreamItem::NetworkStatsUpdated) => continue,
Ok(SyncStreamItem::Audio(audio_buf)) => {
let playback = audio.lock().await;
playback.send_packet(audio_buf);
continue;
}
Err(e) => TsEvent::Error(e.to_string()),
};
if event_tx.send(ts_event).await.is_err() {
break;
}
}
let _ = event_tx.send(TsEvent::Disconnected).await;
}
#[cfg(not(feature = "audio"))]
async fn run_connection(con: SyncConnection, event_tx: mpsc::Sender<TsEvent>) { async fn run_connection(con: SyncConnection, event_tx: mpsc::Sender<TsEvent>) {
let mut stream = con; let mut stream = con;
while let Some(item) = stream.next().await { while let Some(item) = stream.next().await {
@@ -919,6 +987,7 @@ async fn run_connection(con: SyncConnection, event_tx: mpsc::Sender<TsEvent>) {
Ok(SyncStreamItem::IdentityLevelIncreased) => TsEvent::IdentityLevelIncreased, Ok(SyncStreamItem::IdentityLevelIncreased) => TsEvent::IdentityLevelIncreased,
Ok(SyncStreamItem::DisconnectedTemporarily(_)) => TsEvent::DisconnectedTemporarily, Ok(SyncStreamItem::DisconnectedTemporarily(_)) => TsEvent::DisconnectedTemporarily,
Ok(SyncStreamItem::NetworkStatsUpdated) => continue, Ok(SyncStreamItem::NetworkStatsUpdated) => continue,
Ok(_) => continue,
Err(e) => TsEvent::Error(e.to_string()), Err(e) => TsEvent::Error(e.to_string()),
}; };
if event_tx.send(ts_event).await.is_err() { if event_tx.send(ts_event).await.is_err() {
+55 -55
View File
@@ -1,4 +1,4 @@
//! 客户端连接 - 完整握手实现 //! Client connection - full handshake implementation
use std::net::SocketAddr; use std::net::SocketAddr;
use std::time::Duration; use std::time::Duration;
@@ -74,7 +74,7 @@ pub struct HandleResult {
pub events: Vec<CommandEvent>, pub events: Vec<CommandEvent>,
} }
/// 客户端配置 /// Client configuration
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ClientConfig { pub struct ClientConfig {
pub address: SocketAddr, pub address: SocketAddr,
@@ -104,26 +104,26 @@ impl ClientConfig {
} }
} }
/// 客户端连接 /// Client connection
pub struct Client { pub struct Client {
config: ClientConfig, config: ClientConfig,
state_machine: ConnectionStateMachine, state_machine: ConnectionStateMachine,
shared_secret: Option<SharedSecret>, shared_secret: Option<SharedSecret>,
key_cache: KeyCache, key_cache: KeyCache,
client_id: Option<u16>, client_id: Option<u16>,
/// 客户端随机数 A0 /// Client random A0
random0: Option<[u8; 4]>, random0: Option<[u8; 4]>,
/// 服务器随机数 A1 /// Server random A1
random1: Option<[u8; 16]>, random1: Option<[u8; 16]>,
/// A0 反转 /// A0 reversed
random0_r: Option<[u8; 4]>, random0_r: Option<[u8; 4]>,
/// RSA 参数 /// RSA parameters
rsa_x: Option<[u8; 64]>, rsa_x: Option<[u8; 64]>,
rsa_n: Option<[u8; 64]>, rsa_n: Option<[u8; 64]>,
rsa_level: Option<u32>, rsa_level: Option<u32>,
/// 服务器随机数 A2 /// Server random A2
random2: Option<[u8; 100]>, random2: Option<[u8; 100]>,
/// 客户端 alpha /// Client alpha
alpha: Option<[u8; 10]>, alpha: Option<[u8; 10]>,
outgoing_command_id: u16, outgoing_command_id: u16,
outgoing_ack_id: u16, outgoing_ack_id: u16,
@@ -167,18 +167,18 @@ impl Client {
&mut self.key_cache &mut self.key_cache
} }
/// 开始连接握手 /// Start connection handshake
pub fn start_handshake(&mut self) -> Result<Vec<u8>, ProtocolError> { pub fn start_handshake(&mut self) -> Result<Vec<u8>, ProtocolError> {
self.state_machine self.state_machine
.transition(ConnectionState::Connecting) .transition(ConnectionState::Connecting)
.map_err(ProtocolError::PacketParse)?; .map_err(ProtocolError::PacketParse)?;
// 生成随机数 A0 // Generate random A0
let mut random0 = [0u8; 4]; let mut random0 = [0u8; 4];
rand::Rng::fill(&mut rand::thread_rng(), &mut random0); rand::Rng::fill(&mut rand::thread_rng(), &mut random0);
self.random0 = Some(random0); self.random0 = Some(random0);
// 构建 Init0 数据包 // Build Init0 packet
let init = InitPacket { let init = InitPacket {
step: InitStep::Init0, step: InitStep::Init0,
version: Some(Self::encode_version(&self.config.version)), version: Some(Self::encode_version(&self.config.version)),
@@ -198,30 +198,30 @@ impl Client {
Ok(data) Ok(data)
} }
/// 处理接收到的数据 /// Handle received data
pub fn handle_data(&mut self, data: &[u8]) -> Result<HandleResult, ProtocolError> { pub fn handle_data(&mut self, data: &[u8]) -> Result<HandleResult, ProtocolError> {
let mut responses = Vec::new(); let mut responses = Vec::new();
let mut events = Vec::new(); let mut events = Vec::new();
match self.state() { match self.state() {
ConnectionState::Connecting => { ConnectionState::Connecting => {
// 处理 Init1 // Handle Init1
let init = Self::parse_server_init(data)?; let init = Self::parse_server_init(data)?;
if init.step == InitStep::Init1 { if init.step == InitStep::Init1 {
self.random1 = init.random1; self.random1 = init.random1;
self.random0_r = init.random0_r; self.random0_r = init.random0_r;
// 发送 Init2 // Send Init2
let response = self.build_init2()?; let response = self.build_init2()?;
responses.push(response); responses.push(response);
} else if init.step == InitStep::Reset { } else if init.step == InitStep::Reset {
// 服务器要求重置,重新发送 Init0 // Server requested reset, resend Init0
let response = self.start_handshake()?; let response = self.start_handshake()?;
responses.push(response); responses.push(response);
} }
} }
ConnectionState::IdentityLevelIncreasing => { ConnectionState::IdentityLevelIncreasing => {
// 处理 Init3 // Handle Init3
let init = Self::parse_server_init(data)?; let init = Self::parse_server_init(data)?;
if init.step == InitStep::Init3 { if init.step == InitStep::Init3 {
self.rsa_x = init.x; self.rsa_x = init.x;
@@ -229,13 +229,13 @@ impl Client {
self.rsa_level = init.level; self.rsa_level = init.level;
self.random2 = init.random2; self.random2 = init.random2;
// 计算 RSA 解答 // Compute RSA solution
let response = self.build_init4()?; let response = self.build_init4()?;
responses.push(response); responses.push(response);
} }
} }
ConnectionState::Connected => { ConnectionState::Connected => {
// 处理命令数据包 // Handle command packets
let packet = InPacket::parse(Direction::S2C, data)?; let packet = InPacket::parse(Direction::S2C, data)?;
let packet_type = packet.header.flags.packet_type(); let packet_type = packet.header.flags.packet_type();
let content = if !packet.header.flags.is_unencrypted() { let content = if !packet.header.flags.is_unencrypted() {
@@ -270,12 +270,12 @@ impl Client {
responses.push(self.build_ack_packet(packet_type, packet.header.packet_id)?); responses.push(self.build_ack_packet(packet_type, packet.header.packet_id)?);
} }
// 解析命令 // Parse commands
let cmd_str = String::from_utf8_lossy(&content); let cmd_str = String::from_utf8_lossy(&content);
for cmd in Command::parse_many(&cmd_str)? { for cmd in Command::parse_many(&cmd_str)? {
match cmd.name.as_str() { match cmd.name.as_str() {
"initserver" => { "initserver" => {
// 连接完成 // Connection complete
if let Some(id) = cmd.get("client_id") { if let Some(id) = cmd.get("client_id") {
self.client_id = id.parse().ok(); self.client_id = id.parse().ok();
} }
@@ -312,11 +312,11 @@ impl Client {
}); });
} }
"initivexpand" => { "initivexpand" => {
// 旧协议密钥交换 // Old protocol key exchange
responses.extend(self.handle_initivexpand(&cmd)?); responses.extend(self.handle_initivexpand(&cmd)?);
} }
"initivexpand2" => { "initivexpand2" => {
// 新协议密钥交换 // New protocol key exchange
responses.extend(self.handle_initivexpand2(&cmd)?); responses.extend(self.handle_initivexpand2(&cmd)?);
} }
"channellist" => { "channellist" => {
@@ -442,7 +442,7 @@ impl Client {
}] }]
} }
/// 构建 Init2 数据包 /// Build Init2 packet
fn build_init2(&mut self) -> Result<Vec<u8>, ProtocolError> { fn build_init2(&mut self) -> Result<Vec<u8>, ProtocolError> {
let init = InitPacket { let init = InitPacket {
step: InitStep::Init2, step: InitStep::Init2,
@@ -466,27 +466,27 @@ impl Client {
Ok(init.to_c2s_packet_bytes()) Ok(init.to_c2s_packet_bytes())
} }
/// 构建 Init4 数据包 /// Build Init4 packet
fn build_init4(&mut self) -> Result<Vec<u8>, ProtocolError> { fn build_init4(&mut self) -> Result<Vec<u8>, ProtocolError> {
// 计算 y = x^(2^level) mod n // Compute y = x^(2^level) mod n
let x = self let x = self
.rsa_x .rsa_x
.ok_or_else(|| ProtocolError::PacketParse("缺少 RSA x".to_string()))?; .ok_or_else(|| ProtocolError::PacketParse("missing RSA x".to_string()))?;
let n = self let n = self
.rsa_n .rsa_n
.ok_or_else(|| ProtocolError::PacketParse("缺少 RSA n".to_string()))?; .ok_or_else(|| ProtocolError::PacketParse("missing RSA n".to_string()))?;
let level = self let level = self
.rsa_level .rsa_level
.ok_or_else(|| ProtocolError::PacketParse("缺少 RSA level".to_string()))?; .ok_or_else(|| ProtocolError::PacketParse("missing RSA level".to_string()))?;
let y = Self::solve_rsa_puzzle(&x, &n, level); let y = Self::solve_rsa_puzzle(&x, &n, level);
// 生成 alpha // Generate alpha
let mut alpha = [0u8; 10]; let mut alpha = [0u8; 10];
rand::Rng::fill(&mut rand::thread_rng(), &mut alpha); rand::Rng::fill(&mut rand::thread_rng(), &mut alpha);
self.alpha = Some(alpha); self.alpha = Some(alpha);
// 构建 clientinitiv 命令 // Build clientinitiv command
let alpha_b64 = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, alpha); let alpha_b64 = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, alpha);
let omega = self.get_identity_omega()?; let omega = self.get_identity_omega()?;
let ip = self.config.address.ip().to_string(); let ip = self.config.address.ip().to_string();
@@ -520,36 +520,36 @@ impl Client {
Ok(init.to_c2s_packet_bytes()) Ok(init.to_c2s_packet_bytes())
} }
/// 处理 initivexpand (旧协议) /// Handle initivexpand (old protocol)
fn handle_initivexpand(&mut self, cmd: &Command) -> Result<Vec<Vec<u8>>, ProtocolError> { fn handle_initivexpand(&mut self, cmd: &Command) -> Result<Vec<Vec<u8>>, ProtocolError> {
let alpha_b64 = cmd let alpha_b64 = cmd
.get("alpha") .get("alpha")
.ok_or_else(|| ProtocolError::PacketParse("缺少 alpha".to_string()))?; .ok_or_else(|| ProtocolError::PacketParse("missing alpha".to_string()))?;
let beta_b64 = cmd let beta_b64 = cmd
.get("beta") .get("beta")
.ok_or_else(|| ProtocolError::PacketParse("缺少 beta".to_string()))?; .ok_or_else(|| ProtocolError::PacketParse("missing beta".to_string()))?;
let _omega = cmd let _omega = cmd
.get("omega") .get("omega")
.ok_or_else(|| ProtocolError::PacketParse("缺少 omega".to_string()))?; .ok_or_else(|| ProtocolError::PacketParse("missing omega".to_string()))?;
let alpha_bytes = let alpha_bytes =
base64::Engine::decode(&base64::engine::general_purpose::STANDARD, alpha_b64) base64::Engine::decode(&base64::engine::general_purpose::STANDARD, alpha_b64)
.map_err(|_| ProtocolError::PacketParse("无效的 alpha".to_string()))?; .map_err(|_| ProtocolError::PacketParse("invalid alpha".to_string()))?;
let beta_bytes = let beta_bytes =
base64::Engine::decode(&base64::engine::general_purpose::STANDARD, beta_b64) base64::Engine::decode(&base64::engine::general_purpose::STANDARD, beta_b64)
.map_err(|_| ProtocolError::PacketParse("无效的 beta".to_string()))?; .map_err(|_| ProtocolError::PacketParse("invalid beta".to_string()))?;
let mut alpha = [0u8; 10]; let mut alpha = [0u8; 10];
alpha.copy_from_slice(&alpha_bytes); alpha.copy_from_slice(&alpha_bytes);
let mut beta = [0u8; 10]; let mut beta = [0u8; 10];
beta.copy_from_slice(&beta_bytes); beta.copy_from_slice(&beta_bytes);
// 计算共享密钥 // Compute shared secret
let shared_data = [0u8; 32]; // TODO: ECDH 计算 let shared_data = [0u8; 32]; // TODO: Compute from ECDH
let secret = SharedSecret::compute_old(&alpha, &beta, &shared_data); let secret = SharedSecret::compute_old(&alpha, &beta, &shared_data);
self.shared_secret = Some(secret); self.shared_secret = Some(secret);
// 发送 clientek // Send clientek
let ek = self.get_identity_omega()?; let ek = self.get_identity_omega()?;
let proof = self.generate_proof(&ek, beta_b64); let proof = self.generate_proof(&ek, beta_b64);
@@ -563,7 +563,7 @@ impl Client {
]) ])
} }
/// 处理 initivexpand2 (新协议) /// Handle initivexpand2 (new protocol)
/// ///
/// When the server sends a license (`l`), this performs real ECDH key /// When the server sends a license (`l`), this performs real ECDH key
/// exchange using an ephemeral Ed25519 key pair. When no license is /// exchange using an ephemeral Ed25519 key pair. When no license is
@@ -572,14 +572,14 @@ impl Client {
fn handle_initivexpand2(&mut self, cmd: &Command) -> Result<Vec<Vec<u8>>, ProtocolError> { fn handle_initivexpand2(&mut self, cmd: &Command) -> Result<Vec<Vec<u8>>, ProtocolError> {
let beta_b64 = cmd let beta_b64 = cmd
.get("beta") .get("beta")
.ok_or_else(|| ProtocolError::PacketParse("缺少 beta".to_string()))?; .ok_or_else(|| ProtocolError::PacketParse("missing beta".to_string()))?;
let _omega = cmd let _omega = cmd
.get("omega") .get("omega")
.ok_or_else(|| ProtocolError::PacketParse("缺少 omega".to_string()))?; .ok_or_else(|| ProtocolError::PacketParse("missing omega".to_string()))?;
let beta_bytes = let beta_bytes =
base64::Engine::decode(&base64::engine::general_purpose::STANDARD, beta_b64) base64::Engine::decode(&base64::engine::general_purpose::STANDARD, beta_b64)
.map_err(|_| ProtocolError::PacketParse("无效的 beta".to_string()))?; .map_err(|_| ProtocolError::PacketParse("invalid beta".to_string()))?;
let mut beta = [0u8; 54]; let mut beta = [0u8; 54];
if beta_bytes.len() >= 54 { if beta_bytes.len() >= 54 {
@@ -693,7 +693,7 @@ impl Client {
self.build_command_packet(self.build_clientinit()) self.build_command_packet(self.build_clientinit())
} }
/// 构建 clientinit 命令 /// Build clientinit command
pub fn build_clientinit(&self) -> Vec<u8> { pub fn build_clientinit(&self) -> Vec<u8> {
let channel_password = self let channel_password = self
.config .config
@@ -738,9 +738,9 @@ impl Client {
cmd.to_string().into_bytes() cmd.to_string().into_bytes()
} }
/// 编码版本号 /// Encode version number
fn encode_version(version: &str) -> u32 { fn encode_version(version: &str) -> u32 {
// 从版本字符串提取构建时间戳 // Extract build timestamp from version string
if let Some(start) = version.find("[Build: ") { if let Some(start) = version.find("[Build: ") {
let rest = &version[start + 8..]; let rest = &version[start + 8..];
if let Some(end) = rest.find(']') { if let Some(end) = rest.find(']') {
@@ -750,10 +750,10 @@ impl Client {
} }
} }
} }
1466672534 // 默认值 1466672534 // default value
} }
/// 获取当前时间戳 /// Get current timestamp
fn current_timestamp() -> u32 { fn current_timestamp() -> u32 {
std::time::SystemTime::now() std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
@@ -761,14 +761,14 @@ impl Client {
.as_secs() as u32 .as_secs() as u32
} }
/// 解决 RSA 拼图 /// Solve RSA puzzle
/// y = x^(2^level) mod n /// y = x^(2^level) mod n
fn solve_rsa_puzzle(x: &[u8; 64], n: &[u8; 64], level: u32) -> [u8; 64] { fn solve_rsa_puzzle(x: &[u8; 64], n: &[u8; 64], level: u32) -> [u8; 64] {
let x_big = num_bigint::BigUint::from_bytes_be(x); let x_big = num_bigint::BigUint::from_bytes_be(x);
let n_big = num_bigint::BigUint::from_bytes_be(n); let n_big = num_bigint::BigUint::from_bytes_be(n);
// y = x^(2^level) mod n // y = x^(2^level) mod n
// 需要做 level 次平方操作 // Need to perform level squaring operations
let mut y = x_big; let mut y = x_big;
for _ in 0..level { for _ in 0..level {
y = (y.clone() * y) % &n_big; y = (y.clone() * y) % &n_big;
@@ -781,15 +781,15 @@ impl Client {
result result
} }
/// 获取身份公钥 (omega) /// Get identity public key (omega)
fn get_identity_omega(&self) -> Result<String, ProtocolError> { fn get_identity_omega(&self) -> Result<String, ProtocolError> {
self.config self.config
.identity .identity
.public_key_ts_base64() .public_key_ts_base64()
.map_err(|e| ProtocolError::Encryption(format!("身份公钥编码失败: {e}"))) .map_err(|e| ProtocolError::Encryption(format!("identity public key encoding failed: {e}")))
} }
/// 生成证明 /// Generate proof
fn generate_proof(&self, data: &str, beta: &str) -> String { fn generate_proof(&self, data: &str, beta: &str) -> String {
let combined = format!("{}{}", data, beta); let combined = format!("{}{}", data, beta);
self.config.identity.sign_der_base64(combined.as_bytes()) self.config.identity.sign_der_base64(combined.as_bytes())
@@ -827,7 +827,7 @@ mod tests {
#[test] #[test]
fn test_rsa_puzzle() { fn test_rsa_puzzle() {
// 使用非零值测试 // Test with non-zero values
let mut x = [0u8; 64]; let mut x = [0u8; 64];
x[63] = 2; // x = 2 x[63] = 2; // x = 2
let mut n = [0u8; 64]; let mut n = [0u8; 64];
+1 -1
View File
@@ -1,4 +1,4 @@
//! 连接管理 //! Connection management
pub mod client; pub mod client;
pub mod resend; pub mod resend;
+28 -28
View File
@@ -1,9 +1,9 @@
//! 数据包重传和确认系统 //! Packet retransmission and acknowledgment system
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
/// 数据包 ID /// Packet ID
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct PacketId { pub struct PacketId {
pub generation_id: u32, pub generation_id: u32,
@@ -27,7 +27,7 @@ impl PacketId {
} }
} }
/// 已发送的数据包信息 /// Sent packet information
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SentPacket { pub struct SentPacket {
pub data: Vec<u8>, pub data: Vec<u8>,
@@ -42,7 +42,7 @@ impl SentPacket {
data, data,
sent_at: Instant::now(), sent_at: Instant::now(),
retry_count: 0, retry_count: 0,
timeout: Duration::from_millis(500), // 初始超时 500ms timeout: Duration::from_millis(500), // Initial timeout 500ms
} }
} }
@@ -57,18 +57,18 @@ impl SentPacket {
pub fn retry(&mut self) { pub fn retry(&mut self) {
self.retry_count += 1; self.retry_count += 1;
self.sent_at = Instant::now(); self.sent_at = Instant::now();
// 指数退避 // Exponential backoff
self.timeout = Duration::from_millis(500 * (1 << self.retry_count).min(32)); self.timeout = Duration::from_millis(500 * (1 << self.retry_count).min(32));
} }
} }
/// 重传管理器 /// Retransmission manager
pub struct ResendManager { pub struct ResendManager {
/// 等待确认的数据包 /// Packets awaiting acknowledgment
pending: BTreeMap<PacketId, SentPacket>, pending: BTreeMap<PacketId, SentPacket>,
/// 最大重试次数 /// Maximum retry count
max_retries: u32, max_retries: u32,
/// 连接超时 /// Connection timeout
connection_timeout: Duration, connection_timeout: Duration,
} }
@@ -81,17 +81,17 @@ impl ResendManager {
} }
} }
/// 添加已发送的数据包 /// Add sent packet
pub fn add_sent(&mut self, id: PacketId, data: Vec<u8>) { pub fn add_sent(&mut self, id: PacketId, data: Vec<u8>) {
self.pending.insert(id, SentPacket::new(data)); self.pending.insert(id, SentPacket::new(data));
} }
/// 确认数据包 /// Acknowledge packet
pub fn ack(&mut self, id: &PacketId) -> bool { pub fn ack(&mut self, id: &PacketId) -> bool {
self.pending.remove(id).is_some() self.pending.remove(id).is_some()
} }
/// 获取需要重传的数据包 /// Get packets that need retransmission
pub fn get_retransmissions(&mut self) -> Vec<(PacketId, Vec<u8>)> { pub fn get_retransmissions(&mut self) -> Vec<(PacketId, Vec<u8>)> {
let mut retransmissions = Vec::new(); let mut retransmissions = Vec::new();
let mut to_retry = Vec::new(); let mut to_retry = Vec::new();
@@ -112,29 +112,29 @@ impl ResendManager {
retransmissions retransmissions
} }
/// 检查是否连接超时 /// Check if connection timed out
pub fn is_connection_timeout(&self) -> bool { pub fn is_connection_timeout(&self) -> bool {
self.pending self.pending
.values() .values()
.any(|p| p.sent_at.elapsed() > self.connection_timeout) .any(|p| p.sent_at.elapsed() > self.connection_timeout)
} }
/// 获取待确认数据包数量 /// Get number of pending packets
pub fn pending_count(&self) -> usize { pub fn pending_count(&self) -> usize {
self.pending.len() self.pending.len()
} }
/// 清空所有待确认数据包 /// Clear all pending packets
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.pending.clear(); self.pending.clear();
} }
/// 设置最大重试次数 /// Set maximum retry count
pub fn set_max_retries(&mut self, max_retries: u32) { pub fn set_max_retries(&mut self, max_retries: u32) {
self.max_retries = max_retries; self.max_retries = max_retries;
} }
/// 设置连接超时 /// Set connection timeout
pub fn set_connection_timeout(&mut self, timeout: Duration) { pub fn set_connection_timeout(&mut self, timeout: Duration) {
self.connection_timeout = timeout; self.connection_timeout = timeout;
} }
@@ -146,7 +146,7 @@ impl Default for ResendManager {
} }
} }
/// RTT 估算器 /// RTT estimator
pub struct RttEstimator { pub struct RttEstimator {
srtt: Duration, srtt: Duration,
rtt_var: Duration, rtt_var: Duration,
@@ -162,7 +162,7 @@ impl RttEstimator {
} }
} }
/// 更新 RTT 估算 /// Update RTT estimate
pub fn update(&mut self, measured_rtt: Duration) { pub fn update(&mut self, measured_rtt: Duration) {
let alpha = 0.125; let alpha = 0.125;
let beta = 0.25; let beta = 0.25;
@@ -178,7 +178,7 @@ impl RttEstimator {
); );
self.rto = self.srtt + self.rtt_var * 4; self.rto = self.srtt + self.rtt_var * 4;
// 限制 RTO 范围 // Clamp RTO range
if self.rto < Duration::from_millis(100) { if self.rto < Duration::from_millis(100) {
self.rto = Duration::from_millis(100); self.rto = Duration::from_millis(100);
} }
@@ -187,12 +187,12 @@ impl RttEstimator {
} }
} }
/// 获取当前 RTO /// Get current RTO
pub fn rto(&self) -> Duration { pub fn rto(&self) -> Duration {
self.rto self.rto
} }
/// 获取平滑 RTT /// Get smoothed RTT
pub fn srtt(&self) -> Duration { pub fn srtt(&self) -> Duration {
self.srtt self.srtt
} }
@@ -217,7 +217,7 @@ mod tests {
assert_eq!(manager.pending_count(), 1); assert_eq!(manager.pending_count(), 1);
// 确认 // Acknowledge
assert!(manager.ack(&id)); assert!(manager.ack(&id));
assert_eq!(manager.pending_count(), 0); assert_eq!(manager.pending_count(), 0);
} }
@@ -226,17 +226,17 @@ mod tests {
fn test_rtt_estimator() { fn test_rtt_estimator() {
let mut estimator = RttEstimator::new(); let mut estimator = RttEstimator::new();
// 初始 SRTT 500ms // Initial SRTT is 500ms
assert_eq!(estimator.srtt(), Duration::from_millis(500)); assert_eq!(estimator.srtt(), Duration::from_millis(500));
// 更新多次,SRTT 应该逐渐收敛 // Update multiple times, SRTT should converge
for _ in 0..100 { for _ in 0..100 {
estimator.update(Duration::from_millis(100)); estimator.update(Duration::from_millis(100));
} }
// 经过多次更新后,SRTT 应该接近 100ms // After many updates, SRTT should approach 100ms
assert!(estimator.srtt() < Duration::from_millis(150)); assert!(estimator.srtt() < Duration::from_millis(150));
// RTO 应该大于 SRTT // RTO should be greater than SRTT
assert!(estimator.rto() > estimator.srtt()); assert!(estimator.rto() > estimator.srtt());
} }
@@ -245,7 +245,7 @@ mod tests {
let mut packet = SentPacket::new(vec![1, 2, 3]); let mut packet = SentPacket::new(vec![1, 2, 3]);
assert!(!packet.is_expired()); assert!(!packet.is_expired());
// 模拟超时 // Simulate timeout
packet.sent_at = Instant::now() - Duration::from_millis(600); packet.sent_at = Instant::now() - Duration::from_millis(600);
assert!(packet.is_expired()); assert!(packet.is_expired());
assert!(packet.should_retry(10)); assert!(packet.should_retry(10));
+3 -3
View File
@@ -1,8 +1,8 @@
//! 连接状态管理 //! Connection state management
use std::fmt; use std::fmt;
/// 连接状态 /// Connection state
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionState { pub enum ConnectionState {
Disconnected, Disconnected,
@@ -42,7 +42,7 @@ impl fmt::Display for ConnectionState {
} }
} }
/// 连接状态机 /// Connection state machine
pub struct ConnectionStateMachine { pub struct ConnectionStateMachine {
state: ConnectionState, state: ConnectionState,
} }
+8 -8
View File
@@ -1,4 +1,4 @@
//! EAX 模式加密 //! EAX mode encryption
use aes::Aes128; use aes::Aes128;
use eax::aead::consts::U8; use eax::aead::consts::U8;
@@ -9,7 +9,7 @@ use super::keys;
use crate::protocol::{InPacket, OutPacket}; use crate::protocol::{InPacket, OutPacket};
use crate::ProtocolError; use crate::ProtocolError;
/// EAX 加密器 /// EAX cipher
pub struct EaxCipher { pub struct EaxCipher {
cipher: Eax<Aes128, U8>, cipher: Eax<Aes128, U8>,
} }
@@ -32,7 +32,7 @@ impl EaxCipher {
let tag = self let tag = self
.cipher .cipher
.encrypt_in_place_detached(nonce, header, data) .encrypt_in_place_detached(nonce, header, data)
.map_err(|_| ProtocolError::Encryption("EAX 加密失败".to_string()))?; .map_err(|_| ProtocolError::Encryption("EAX encryption failed".to_string()))?;
let mut mac = [0u8; 8]; let mut mac = [0u8; 8];
mac.copy_from_slice(&tag[..8]); mac.copy_from_slice(&tag[..8]);
@@ -51,11 +51,11 @@ impl EaxCipher {
self.cipher self.cipher
.decrypt_in_place_detached(nonce, header, data, tag) .decrypt_in_place_detached(nonce, header, data, tag)
.map_err(|_| ProtocolError::Decryption("MAC 验证失败".to_string())) .map_err(|_| ProtocolError::Decryption("MAC verification failed".to_string()))
} }
} }
/// 加密数据包 /// Encrypt packet
pub fn encrypt_packet( pub fn encrypt_packet(
packet: &mut OutPacket, packet: &mut OutPacket,
generation_id: u32, generation_id: u32,
@@ -77,7 +77,7 @@ pub fn encrypt_packet(
Ok(()) Ok(())
} }
/// 解密数据包 /// Decrypt packet
pub fn decrypt_packet( pub fn decrypt_packet(
packet: &InPacket, packet: &InPacket,
generation_id: u32, generation_id: u32,
@@ -99,7 +99,7 @@ pub fn decrypt_packet(
Ok(data) Ok(data)
} }
/// 假加密 /// Fake encryption
pub fn encrypt_fake(packet: &mut OutPacket) -> Result<(), ProtocolError> { pub fn encrypt_fake(packet: &mut OutPacket) -> Result<(), ProtocolError> {
let cipher = EaxCipher::new(&keys::FAKE_KEY); let cipher = EaxCipher::new(&keys::FAKE_KEY);
let meta = packet.header.get_meta(packet.direction); let meta = packet.header.get_meta(packet.direction);
@@ -108,7 +108,7 @@ pub fn encrypt_fake(packet: &mut OutPacket) -> Result<(), ProtocolError> {
Ok(()) Ok(())
} }
/// 假解密 /// Fake decryption
pub fn decrypt_fake(packet: &InPacket) -> Result<Vec<u8>, ProtocolError> { pub fn decrypt_fake(packet: &InPacket) -> Result<Vec<u8>, ProtocolError> {
let cipher = EaxCipher::new(&keys::FAKE_KEY); let cipher = EaxCipher::new(&keys::FAKE_KEY);
let meta = packet.header.get_meta(packet.direction); let meta = packet.header.get_meta(packet.direction);
+5 -5
View File
@@ -1,9 +1,9 @@
//! 哈希函数 //! Hash functions
use sha1::Sha1; use sha1::Sha1;
use sha2::{Digest, Sha256, Sha512}; use sha2::{Digest, Sha256, Sha512};
/// SHA-1 哈希 /// SHA-1 hash
pub fn sha1(data: &[u8]) -> [u8; 20] { pub fn sha1(data: &[u8]) -> [u8; 20] {
let mut hasher = Sha1::new(); let mut hasher = Sha1::new();
hasher.update(data); hasher.update(data);
@@ -13,7 +13,7 @@ pub fn sha1(data: &[u8]) -> [u8; 20] {
hash hash
} }
/// SHA-256 哈希 /// SHA-256 hash
pub fn sha256(data: &[u8]) -> [u8; 32] { pub fn sha256(data: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(data); hasher.update(data);
@@ -23,7 +23,7 @@ pub fn sha256(data: &[u8]) -> [u8; 32] {
hash hash
} }
/// SHA-512 哈希 /// SHA-512 hash
pub fn sha512(data: &[u8]) -> [u8; 64] { pub fn sha512(data: &[u8]) -> [u8; 64] {
let mut hasher = Sha512::new(); let mut hasher = Sha512::new();
hasher.update(data); hasher.update(data);
@@ -33,7 +33,7 @@ pub fn sha512(data: &[u8]) -> [u8; 64] {
hash hash
} }
/// 计算密码哈希 /// Compute password hash
pub fn hash_password(password: &str) -> String { pub fn hash_password(password: &str) -> String {
let hash = sha1(password.as_bytes()); let hash = sha1(password.as_bytes());
base64::Engine::encode(&base64::engine::general_purpose::STANDARD, hash) base64::Engine::encode(&base64::engine::general_purpose::STANDARD, hash)
+11 -11
View File
@@ -1,4 +1,4 @@
//! 密钥管理 //! Key management
use sha1::Sha1; use sha1::Sha1;
use sha2::{Digest, Sha256, Sha512}; use sha2::{Digest, Sha256, Sha512};
@@ -6,19 +6,19 @@ use sha2::{Digest, Sha256, Sha512};
use crate::protocol::Direction; use crate::protocol::Direction;
use crate::protocol::PacketType; use crate::protocol::PacketType;
/// 假加密密钥 /// Fake encryption key
pub const FAKE_KEY: [u8; 16] = *b"c:\\windows\\syste"; pub const FAKE_KEY: [u8; 16] = *b"c:\\windows\\syste";
/// 假加密 Nonce /// Fake encryption nonce
pub const FAKE_NONCE: [u8; 16] = *b"m\\firewall32.cpl"; pub const FAKE_NONCE: [u8; 16] = *b"m\\firewall32.cpl";
/// 许可证根密钥 /// License root key
pub const ROOT_KEY: [u8; 32] = [ pub const ROOT_KEY: [u8; 32] = [
0xcd, 0x0d, 0xe2, 0xae, 0xd4, 0x63, 0x45, 0x50, 0x9a, 0x7e, 0x3c, 0xfd, 0x8f, 0x68, 0xb3, 0xdc, 0xcd, 0x0d, 0xe2, 0xae, 0xd4, 0x63, 0x45, 0x50, 0x9a, 0x7e, 0x3c, 0xfd, 0x8f, 0x68, 0xb3, 0xdc,
0x75, 0x55, 0xb2, 0x9d, 0xcc, 0xec, 0x73, 0xcd, 0x18, 0x75, 0x0f, 0x99, 0x38, 0x12, 0x40, 0x8a, 0x75, 0x55, 0xb2, 0x9d, 0xcc, 0xec, 0x73, 0xcd, 0x18, 0x75, 0x0f, 0x99, 0x38, 0x12, 0x40, 0x8a,
]; ];
/// 共享密钥 /// Shared secret
#[derive(Clone)] #[derive(Clone)]
pub struct SharedSecret { pub struct SharedSecret {
pub iv: [u8; 64], pub iv: [u8; 64],
@@ -87,7 +87,7 @@ impl std::fmt::Debug for SharedSecret {
} }
} }
/// 缓存的密钥 /// Cached key
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CachedKey { pub struct CachedKey {
pub generation_id: u32, pub generation_id: u32,
@@ -115,7 +115,7 @@ impl Default for CachedKey {
} }
} }
/// 密钥缓存 /// Key cache
pub struct KeyCache { pub struct KeyCache {
cache: [[CachedKey; 2]; 8], cache: [[CachedKey; 2]; 8],
} }
@@ -162,7 +162,7 @@ impl Default for KeyCache {
} }
} }
/// 创建密钥和 Nonce /// Create key and nonce
pub fn create_key_nonce( pub fn create_key_nonce(
packet_type: PacketType, packet_type: PacketType,
direction: Direction, direction: Direction,
@@ -192,7 +192,7 @@ pub fn create_key_nonce(
(key, nonce) (key, nonce)
} }
/// 创建用于加密的密钥 /// Create encryption key
pub fn create_encryption_key(key: &[u8; 16], packet_id: u16) -> [u8; 16] { pub fn create_encryption_key(key: &[u8; 16], packet_id: u16) -> [u8; 16] {
let mut result = *key; let mut result = *key;
result[0] ^= (packet_id >> 8) as u8; result[0] ^= (packet_id >> 8) as u8;
@@ -200,7 +200,7 @@ pub fn create_encryption_key(key: &[u8; 16], packet_id: u16) -> [u8; 16] {
result result
} }
/// 计算 Hash Cash 级别 /// Compute hash cash level
pub fn get_hash_cash_level(omega: &str, offset: u64) -> u8 { pub fn get_hash_cash_level(omega: &str, offset: u64) -> u8 {
let mut hasher = Sha1::new(); let mut hasher = Sha1::new();
hasher.update(format!("{}{}", omega, offset).as_bytes()); hasher.update(format!("{}{}", omega, offset).as_bytes());
@@ -219,7 +219,7 @@ pub fn get_hash_cash_level(omega: &str, offset: u64) -> u8 {
level level
} }
/// 计算 UID /// Compute UID
pub fn compute_uid(public_key: &[u8]) -> String { pub fn compute_uid(public_key: &[u8]) -> String {
let mut hasher = Sha1::new(); let mut hasher = Sha1::new();
hasher.update(public_key); hasher.update(public_key);
+1 -1
View File
@@ -1,4 +1,4 @@
//! 加密模块 //! Cryptography module
pub mod eax; pub mod eax;
pub mod ephemeral; pub mod ephemeral;
+10 -10
View File
@@ -1,4 +1,4 @@
//! 加密测试 //! Cryptography tests
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@@ -50,7 +50,7 @@ mod tests {
let encrypted = create_encryption_key(&key, 0x1234); let encrypted = create_encryption_key(&key, 0x1234);
assert_eq!(encrypted[0], key[0] ^ 0x12); assert_eq!(encrypted[0], key[0] ^ 0x12);
assert_eq!(encrypted[1], key[1] ^ 0x34); assert_eq!(encrypted[1], key[1] ^ 0x34);
// 其他字节不变 // Other bytes unchanged
assert_eq!(encrypted[2], key[2]); assert_eq!(encrypted[2], key[2]);
} }
@@ -86,7 +86,7 @@ mod tests {
assert_eq!(key1, key2); assert_eq!(key1, key2);
assert_eq!(nonce1, nonce2); assert_eq!(nonce1, nonce2);
// 不同的 generation_id 应该返回不同的密钥 // Different generation_id should return different keys
let (key3, _) = cache.get_or_create(PacketType::Command, Direction::C2S, 1, &iv); let (key3, _) = cache.get_or_create(PacketType::Command, Direction::C2S, 1, &iv);
assert_ne!(key1, key3); assert_ne!(key1, key3);
} }
@@ -106,10 +106,10 @@ mod tests {
let header = b"test header"; let header = b"test header";
let mut data = b"Hello, World!".to_vec(); let mut data = b"Hello, World!".to_vec();
// 加密 // Encrypt
let mac = cipher.encrypt(&nonce, header, &mut data).unwrap(); let mac = cipher.encrypt(&nonce, header, &mut data).unwrap();
// 解密 // Decrypt
cipher.decrypt(&nonce, header, &mut data, &mac).unwrap(); cipher.decrypt(&nonce, header, &mut data, &mac).unwrap();
assert_eq!(data, b"Hello, World!"); assert_eq!(data, b"Hello, World!");
@@ -124,10 +124,10 @@ mod tests {
); );
packet.header.packet_id = 1; packet.header.packet_id = 1;
// 假加密 // Fake encryption
encrypt_fake(&mut packet).unwrap(); encrypt_fake(&mut packet).unwrap();
// 假解密 // Fake decryption
let in_packet = InPacket { let in_packet = InPacket {
direction: Direction::C2S, direction: Direction::C2S,
header: packet.header.clone(), header: packet.header.clone(),
@@ -140,13 +140,13 @@ mod tests {
#[test] #[test]
fn test_hash_cash_level() { fn test_hash_cash_level() {
// 测试不同的 offset 产生不同的 level // Test that different offsets produce different levels
let level0 = get_hash_cash_level("test_key", 0); let level0 = get_hash_cash_level("test_key", 0);
let level1 = get_hash_cash_level("test_key", 1); let level1 = get_hash_cash_level("test_key", 1);
assert!(level0 <= 160); assert!(level0 <= 160);
assert!(level1 <= 160); assert!(level1 <= 160);
// 使用一个会产生更高 level 的 key // Use a key that produces a higher level
let level_high = get_hash_cash_level("a", 12345); let level_high = get_hash_cash_level("a", 12345);
assert!(level_high <= 160); assert!(level_high <= 160);
} }
@@ -156,7 +156,7 @@ mod tests {
let public_key = b"test_public_key_data"; let public_key = b"test_public_key_data";
let uid = compute_uid(public_key); let uid = compute_uid(public_key);
assert!(!uid.is_empty()); assert!(!uid.is_empty());
// UID 应该是 base64 编码的 SHA1 哈希 // UID should be a base64-encoded SHA1 hash
assert!(uid.len() > 20); assert!(uid.len() > 20);
} }
} }
+19 -19
View File
@@ -1,4 +1,4 @@
//! TeamSpeak 3 协议核心实现 //! TeamSpeak 3 protocol core implementation
pub mod connection; pub mod connection;
pub mod crypto; pub mod crypto;
@@ -14,59 +14,59 @@ pub use query::*;
use thiserror::Error; use thiserror::Error;
/// 协议错误 /// Protocol error
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ProtocolError { pub enum ProtocolError {
#[error("数据包解析错误: {0}")] #[error("packet parse error: {0}")]
PacketParse(String), PacketParse(String),
#[error("加密错误: {0}")] #[error("encryption error: {0}")]
Encryption(String), Encryption(String),
#[error("解密错误: {0}")] #[error("decryption error: {0}")]
Decryption(String), Decryption(String),
#[error("压缩错误: {0}")] #[error("compression error: {0}")]
Compression(String), Compression(String),
#[error("解压错误: {0}")] #[error("decompression error: {0}")]
Decompression(String), Decompression(String),
#[error("无效的数据包类型: {0}")] #[error("invalid packet type: {0}")]
InvalidPacketType(u8), InvalidPacketType(u8),
#[error("无效的标志位: {0}")] #[error("invalid flags: {0}")]
InvalidFlags(u8), InvalidFlags(u8),
#[error("数据包过大: {size} > {max}")] #[error("packet too large: {size} > {max}")]
PacketTooLarge { size: usize, max: usize }, PacketTooLarge { size: usize, max: usize },
#[error("数据包过小: {size} < {min}")] #[error("packet too small: {size} < {min}")]
PacketTooSmall { size: usize, min: usize }, PacketTooSmall { size: usize, min: usize },
#[error("无效的客户端 ID: {0}")] #[error("invalid client ID: {0}")]
InvalidClientId(u16), InvalidClientId(u16),
#[error("无效的数据包 ID: {0}")] #[error("invalid packet ID: {0}")]
InvalidPacketId(u16), InvalidPacketId(u16),
#[error("MAC 验证失败")] #[error("MAC verification failed")]
MacVerificationFailed, MacVerificationFailed,
#[error("超时: {0}")] #[error("timeout: {0}")]
Timeout(String), Timeout(String),
#[error("连接关闭")] #[error("connection closed")]
ConnectionClosed, ConnectionClosed,
#[error("命令错误: {0}")] #[error("command error: {0}")]
Command(String), Command(String),
#[error("网络错误: {0}")] #[error("network error: {0}")]
Network(#[from] std::io::Error), Network(#[from] std::io::Error),
} }
/// 协议结果类型 /// Protocol result type
pub type ProtocolResult<T> = Result<T, ProtocolError>; pub type ProtocolResult<T> = Result<T, ProtocolError>;
impl From<protocol::CommandError> for ProtocolError { impl From<protocol::CommandError> for ProtocolError {
+1 -1
View File
@@ -1,4 +1,4 @@
//! 网络模块 //! Network module
pub mod resolver; pub mod resolver;
pub mod socket; pub mod socket;
+8 -8
View File
@@ -1,15 +1,15 @@
//! 地址解析 //! Address resolution
use std::net::SocketAddr; use std::net::SocketAddr;
/// 服务器地址 /// Server address
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ServerAddress { pub enum ServerAddress {
/// 直接 IP 地址 /// Direct IP address
Ip(SocketAddr), Ip(SocketAddr),
/// 域名 /// Domain name
Domain(String), Domain(String),
/// 服务器昵称 /// Server nickname
Nickname(String), Nickname(String),
} }
@@ -24,15 +24,15 @@ impl ServerAddress {
} }
async fn resolve_domain(domain: &str) -> Result<SocketAddr, Box<dyn std::error::Error>> { async fn resolve_domain(domain: &str) -> Result<SocketAddr, Box<dyn std::error::Error>> {
// 尝试直接解析 // Try direct resolution
let addrs = tokio::net::lookup_host(format!("{}:9987", domain)).await?; let addrs = tokio::net::lookup_host(format!("{}:9987", domain)).await?;
addrs addrs
.into_iter() .into_iter()
.next() .next()
.ok_or_else(|| "无法解析域名".into()) .ok_or_else(|| "failed to resolve domain".into())
} }
async fn resolve_nickname(nickname: &str) -> Result<SocketAddr, Box<dyn std::error::Error>> { async fn resolve_nickname(nickname: &str) -> Result<SocketAddr, Box<dyn std::error::Error>> {
// TODO: 实现 TSDNS 和昵称解析 // TODO: Implement TSDNS and nickname resolution
resolve_domain(nickname).await resolve_domain(nickname).await
} }
+2 -2
View File
@@ -1,4 +1,4 @@
//! UDP Socket 抽象 //! UDP Socket abstraction
use std::net::SocketAddr; use std::net::SocketAddr;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -26,7 +26,7 @@ pub trait Socket {
fn local_addr(&self) -> std::io::Result<SocketAddr>; fn local_addr(&self) -> std::io::Result<SocketAddr>;
} }
/// UDP Socket 实现 /// UDP Socket implementation
pub struct UdpSocketWrapper { pub struct UdpSocketWrapper {
socket: UdpSocket, socket: UdpSocket,
} }
+14 -14
View File
@@ -1,8 +1,8 @@
//! 命令解析和序列化 //! Command parsing and serialization
use std::fmt; use std::fmt;
/// 命令解析错误 /// Command parsing error
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum CommandError { pub enum CommandError {
InvalidFormat(String), InvalidFormat(String),
@@ -14,12 +14,12 @@ pub enum CommandError {
impl fmt::Display for CommandError { impl fmt::Display for CommandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::InvalidFormat(msg) => write!(f, "无效的命令格式: {}", msg), Self::InvalidFormat(msg) => write!(f, "invalid command format: {}", msg),
Self::MissingParameter(name) => write!(f, "缺少必需的参数: {}", name), Self::MissingParameter(name) => write!(f, "missing required parameter: {}", name),
Self::InvalidParameterValue { name, value } => { Self::InvalidParameterValue { name, value } => {
write!(f, "无效的参数值: {}={}", name, value) write!(f, "invalid parameter value: {}={}", name, value)
} }
Self::EscapeError(msg) => write!(f, "转义序列错误: {}", msg), Self::EscapeError(msg) => write!(f, "escape sequence error: {}", msg),
} }
} }
} }
@@ -28,7 +28,7 @@ impl std::error::Error for CommandError {}
pub type CommandResult<T> = Result<T, CommandError>; pub type CommandResult<T> = Result<T, CommandError>;
/// 转义序列处理 /// Escape sequence handling
pub mod escape { pub mod escape {
use super::CommandError; use super::CommandError;
@@ -65,12 +65,12 @@ pub mod escape {
Some('t') => result.push('\t'), Some('t') => result.push('\t'),
Some(other) => { Some(other) => {
return Err(CommandError::EscapeError(format!( return Err(CommandError::EscapeError(format!(
"未知的转义序列: \\{}", "unknown escape sequence: \\{}",
other other
))) )))
} }
None => { None => {
return Err(CommandError::EscapeError("意外的转义序列结束".to_string())) return Err(CommandError::EscapeError("unexpected end of escape sequence".to_string()))
} }
} }
} else { } else {
@@ -82,7 +82,7 @@ pub mod escape {
} }
} }
/// 命令参数 /// Command argument
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CommandArgument { pub struct CommandArgument {
pub name: String, pub name: String,
@@ -126,7 +126,7 @@ impl fmt::Display for CommandArgument {
} }
} }
/// 命令 /// Command
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Command { pub struct Command {
pub name: String, pub name: String,
@@ -177,7 +177,7 @@ impl Command {
pub fn parse(input: &str) -> CommandResult<Self> { pub fn parse(input: &str) -> CommandResult<Self> {
let input = input.trim(); let input = input.trim();
if input.is_empty() { if input.is_empty() {
return Err(CommandError::InvalidFormat("空命令".to_string())); return Err(CommandError::InvalidFormat("empty command".to_string()));
} }
let parts: Vec<&str> = input.splitn(2, ' ').collect(); let parts: Vec<&str> = input.splitn(2, ' ').collect();
@@ -208,7 +208,7 @@ impl Command {
pub fn parse_many(input: &str) -> CommandResult<Vec<Self>> { pub fn parse_many(input: &str) -> CommandResult<Vec<Self>> {
let input = input.trim(); let input = input.trim();
if input.is_empty() { if input.is_empty() {
return Err(CommandError::InvalidFormat("空命令".to_string())); return Err(CommandError::InvalidFormat("empty command".to_string()));
} }
let parts: Vec<&str> = input.splitn(2, ' ').collect(); let parts: Vec<&str> = input.splitn(2, ' ').collect();
@@ -242,7 +242,7 @@ impl fmt::Display for Command {
} }
} }
/// 命令构建器 /// Command builder
pub struct CommandBuilder { pub struct CommandBuilder {
command: Command, command: Command,
} }
+1 -1
View File
@@ -1,4 +1,4 @@
//! 协议模块 //! Protocol module
pub mod commands; pub mod commands;
pub mod packet; pub mod packet;
+12 -12
View File
@@ -1,24 +1,24 @@
//! 数据包定义和处理 //! Packet definition and handling
use std::fmt; use std::fmt;
use super::types::*; use super::types::*;
use crate::ProtocolError; use crate::ProtocolError;
/// 最大数据包大小 /// Maximum packet size
pub const MAX_PACKET_SIZE: usize = 500; pub const MAX_PACKET_SIZE: usize = 500;
/// C2S 头部大小 /// C2S header size
pub const C2S_HEADER_SIZE: usize = 13; // 8 (MAC) + 2 (PId) + 2 (CId) + 1 (PT) pub const C2S_HEADER_SIZE: usize = 13; // 8 (MAC) + 2 (PId) + 2 (CId) + 1 (PT)
/// S2C 头部大小 /// S2C header size
pub const S2C_HEADER_SIZE: usize = 11; // 8 (MAC) + 2 (PId) + 1 (PT) pub const S2C_HEADER_SIZE: usize = 11; // 8 (MAC) + 2 (PId) + 1 (PT)
/// Init packets use a fixed MAC and packet id during the TS3 handshake. /// Init packets use a fixed MAC and packet id during the TS3 handshake.
pub const INIT_MAC: [u8; 8] = *b"TS3INIT1"; pub const INIT_MAC: [u8; 8] = *b"TS3INIT1";
pub const INIT_PACKET_ID: u16 = 0x65; pub const INIT_PACKET_ID: u16 = 0x65;
/// 数据包方向 /// Packet direction
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction { pub enum Direction {
C2S, C2S,
@@ -34,7 +34,7 @@ impl Direction {
} }
} }
/// 数据包标志位 /// Packet flags
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Flags(pub u8); pub struct Flags(pub u8);
@@ -120,7 +120,7 @@ impl fmt::Display for Flags {
} }
} }
/// 数据包头部 /// Packet header
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Header { pub struct Header {
pub mac: [u8; 8], pub mac: [u8; 8],
@@ -218,7 +218,7 @@ impl Header {
} }
} }
/// 输入数据包 /// Inbound packet
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InPacket { pub struct InPacket {
pub direction: Direction, pub direction: Direction,
@@ -256,7 +256,7 @@ impl InPacket {
} }
} }
/// 输出数据包 /// Outbound packet
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct OutPacket { pub struct OutPacket {
pub direction: Direction, pub direction: Direction,
@@ -326,7 +326,7 @@ impl OutPacket {
} }
} }
/// 确认数据包 /// Acknowledgment packet
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AckPacket { pub struct AckPacket {
pub direction: Direction, pub direction: Direction,
@@ -351,7 +351,7 @@ impl AckPacket {
} }
} }
/// 初始化步骤 /// Init step
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InitStep { pub enum InitStep {
Init0, Init0,
@@ -362,7 +362,7 @@ pub enum InitStep {
Reset, Reset,
} }
/// 初始化数据包 /// Init packet
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InitPacket { pub struct InitPacket {
pub step: InitStep, pub step: InitStep,
+1 -1
View File
@@ -1,4 +1,4 @@
//! 数据包处理测试 //! Packet processing tests
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
+5 -5
View File
@@ -1,8 +1,8 @@
//! 协议类型定义 //! Protocol type definitions
use std::fmt; use std::fmt;
/// 数据包类型 /// Packet type
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PacketType { pub enum PacketType {
Voice, Voice,
@@ -111,7 +111,7 @@ impl fmt::Display for PacketType {
} }
} }
/// 编解码器类型 /// Codec type
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CodecType { pub enum CodecType {
SpeexNarrowband, SpeexNarrowband,
@@ -163,7 +163,7 @@ impl CodecType {
} }
} }
/// 私语类型 /// Whisper type
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GroupWhisperType { pub enum GroupWhisperType {
ServerGroup, ServerGroup,
@@ -193,7 +193,7 @@ impl GroupWhisperType {
} }
} }
/// 私语目标 /// Whisper target
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GroupWhisperTarget { pub enum GroupWhisperTarget {
AllChannels, AllChannels,
+2 -9
View File
@@ -1,11 +1,10 @@
//! 书签管理 //! Bookmark management
use chrono::Utc; use chrono::Utc;
use rusqlite::params; use rusqlite::params;
use super::{DatabaseError, DatabaseManager, DatabaseResult}; use super::{DatabaseError, DatabaseManager, DatabaseResult};
/// 书签信息
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Bookmark { pub struct Bookmark {
pub id: String, pub id: String,
@@ -24,7 +23,6 @@ pub struct Bookmark {
} }
impl DatabaseManager { impl DatabaseManager {
/// 创建书签
pub fn create_bookmark( pub fn create_bookmark(
&self, &self,
name: &str, name: &str,
@@ -57,7 +55,6 @@ impl DatabaseManager {
}) })
} }
/// 获取书签
pub fn get_bookmark(&self, id: &str) -> DatabaseResult<Bookmark> { pub fn get_bookmark(&self, id: &str) -> DatabaseResult<Bookmark> {
let conn = self.connection(); let conn = self.connection();
let mut stmt = conn.prepare( let mut stmt = conn.prepare(
@@ -82,12 +79,11 @@ impl DatabaseManager {
updated_at: row.get(12)?, updated_at: row.get(12)?,
}) })
}) })
.map_err(|_| DatabaseError::NotFound(format!("书签 {} 未找到", id)))?; .map_err(|_| DatabaseError::NotFound(format!("Bookmark {} not found", id)))?;
Ok(bookmark) Ok(bookmark)
} }
/// 获取所有书签
pub fn get_all_bookmarks(&self) -> DatabaseResult<Vec<Bookmark>> { pub fn get_all_bookmarks(&self) -> DatabaseResult<Vec<Bookmark>> {
let conn = self.connection(); let conn = self.connection();
let mut stmt = conn.prepare( let mut stmt = conn.prepare(
@@ -117,7 +113,6 @@ impl DatabaseManager {
Ok(bookmarks) Ok(bookmarks)
} }
/// 更新书签
pub fn update_bookmark( pub fn update_bookmark(
&self, &self,
id: &str, id: &str,
@@ -159,14 +154,12 @@ impl DatabaseManager {
Ok(()) Ok(())
} }
/// 删除书签
pub fn delete_bookmark(&self, id: &str) -> DatabaseResult<()> { pub fn delete_bookmark(&self, id: &str) -> DatabaseResult<()> {
self.connection() self.connection()
.execute("DELETE FROM bookmarks WHERE id = ?1", params![id])?; .execute("DELETE FROM bookmarks WHERE id = ?1", params![id])?;
Ok(()) Ok(())
} }
/// 更新最后连接时间
pub fn update_bookmark_last_connected(&self, id: &str) -> DatabaseResult<()> { pub fn update_bookmark_last_connected(&self, id: &str) -> DatabaseResult<()> {
let now = Utc::now().to_rfc3339(); let now = Utc::now().to_rfc3339();
self.connection().execute( self.connection().execute(
+1 -1
View File
@@ -1,4 +1,4 @@
//! 配置管理 //! Configuration management
use chrono::Utc; use chrono::Utc;
use rusqlite::params; use rusqlite::params;
+2 -8
View File
@@ -1,11 +1,10 @@
//! 身份管理 //! Identity management
use chrono::Utc; use chrono::Utc;
use rusqlite::params; use rusqlite::params;
use super::{DatabaseError, DatabaseManager, DatabaseResult}; use super::{DatabaseError, DatabaseManager, DatabaseResult};
/// 身份信息
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Identity { pub struct Identity {
pub id: String, pub id: String,
@@ -18,7 +17,6 @@ pub struct Identity {
} }
impl DatabaseManager { impl DatabaseManager {
/// 创建身份
pub fn create_identity(&self, name: &str, private_key: &str) -> DatabaseResult<Identity> { pub fn create_identity(&self, name: &str, private_key: &str) -> DatabaseResult<Identity> {
let id = uuid::Uuid::new_v4().to_string(); let id = uuid::Uuid::new_v4().to_string();
let now = Utc::now().to_rfc3339(); let now = Utc::now().to_rfc3339();
@@ -39,7 +37,6 @@ impl DatabaseManager {
}) })
} }
/// 获取身份
pub fn get_identity(&self, id: &str) -> DatabaseResult<Identity> { pub fn get_identity(&self, id: &str) -> DatabaseResult<Identity> {
let conn = self.connection(); let conn = self.connection();
let mut stmt = conn.prepare( let mut stmt = conn.prepare(
@@ -58,12 +55,11 @@ impl DatabaseManager {
updated_at: row.get(6)?, updated_at: row.get(6)?,
}) })
}) })
.map_err(|_| DatabaseError::NotFound(format!("身份 {} 未找到", id)))?; .map_err(|_| DatabaseError::NotFound(format!("Identity {} not found", id)))?;
Ok(identity) Ok(identity)
} }
/// 获取所有身份
pub fn get_all_identities(&self) -> DatabaseResult<Vec<Identity>> { pub fn get_all_identities(&self) -> DatabaseResult<Vec<Identity>> {
let conn = self.connection(); let conn = self.connection();
let mut stmt = conn.prepare( let mut stmt = conn.prepare(
@@ -87,7 +83,6 @@ impl DatabaseManager {
Ok(identities) Ok(identities)
} }
/// 更新身份
pub fn update_identity( pub fn update_identity(
&self, &self,
id: &str, id: &str,
@@ -113,7 +108,6 @@ impl DatabaseManager {
Ok(()) Ok(())
} }
/// 删除身份
pub fn delete_identity(&self, id: &str) -> DatabaseResult<()> { pub fn delete_identity(&self, id: &str) -> DatabaseResult<()> {
self.connection() self.connection()
.execute("DELETE FROM identities WHERE id = ?1", params![id])?; .execute("DELETE FROM identities WHERE id = ?1", params![id])?;
+6 -9
View File
@@ -1,4 +1,4 @@
//! 数据存储 //! Database storage
pub mod bookmark; pub mod bookmark;
pub mod config; pub mod config;
@@ -11,29 +11,26 @@ pub use message::*;
use thiserror::Error; use thiserror::Error;
/// 数据库错误
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum DatabaseError { pub enum DatabaseError {
#[error("SQLite 错误: {0}")] #[error("SQLite error: {0}")]
Sqlite(#[from] rusqlite::Error), Sqlite(#[from] rusqlite::Error),
#[error("序列化错误: {0}")] #[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error), Serialization(#[from] serde_json::Error),
#[error("IO 错误: {0}")] #[error("IO error: {0}")]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
#[error("未找到: {0}")] #[error("Not found: {0}")]
NotFound(String), NotFound(String),
#[error("已存在: {0}")] #[error("Already exists: {0}")]
AlreadyExists(String), AlreadyExists(String),
} }
/// 数据库结果类型
pub type DatabaseResult<T> = Result<T, DatabaseError>; pub type DatabaseResult<T> = Result<T, DatabaseError>;
/// 数据库管理器
pub struct DatabaseManager { pub struct DatabaseManager {
conn: rusqlite::Connection, conn: rusqlite::Connection,
} }
+2 -9
View File
@@ -1,11 +1,10 @@
//! 消息管理 //! Message management
use chrono::Utc; use chrono::Utc;
use rusqlite::params; use rusqlite::params;
use super::{DatabaseError, DatabaseManager, DatabaseResult}; use super::{DatabaseError, DatabaseManager, DatabaseResult};
/// 消息信息
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Message { pub struct Message {
pub id: i64, pub id: i64,
@@ -21,7 +20,6 @@ pub struct Message {
} }
impl DatabaseManager { impl DatabaseManager {
/// 创建消息
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn create_message( pub fn create_message(
&self, &self,
@@ -56,7 +54,6 @@ impl DatabaseManager {
}) })
} }
/// 获取消息
pub fn get_message(&self, id: i64) -> DatabaseResult<Message> { pub fn get_message(&self, id: i64) -> DatabaseResult<Message> {
let conn = self.connection(); let conn = self.connection();
let mut stmt = conn.prepare( let mut stmt = conn.prepare(
@@ -78,12 +75,11 @@ impl DatabaseManager {
timestamp: row.get(9)?, timestamp: row.get(9)?,
}) })
}) })
.map_err(|_| DatabaseError::NotFound(format!("消息 {} 未找到", id)))?; .map_err(|_| DatabaseError::NotFound(format!("Message {} not found", id)))?;
Ok(message) Ok(message)
} }
/// 获取服务器消息
pub fn get_server_messages( pub fn get_server_messages(
&self, &self,
server_address: &str, server_address: &str,
@@ -115,21 +111,18 @@ impl DatabaseManager {
Ok(messages) Ok(messages)
} }
/// 标记消息为已读
pub fn mark_message_read(&self, id: i64) -> DatabaseResult<()> { pub fn mark_message_read(&self, id: i64) -> DatabaseResult<()> {
self.connection() self.connection()
.execute("UPDATE messages SET is_read = 1 WHERE id = ?1", params![id])?; .execute("UPDATE messages SET is_read = 1 WHERE id = ?1", params![id])?;
Ok(()) Ok(())
} }
/// 删除消息
pub fn delete_message(&self, id: i64) -> DatabaseResult<()> { pub fn delete_message(&self, id: i64) -> DatabaseResult<()> {
self.connection() self.connection()
.execute("DELETE FROM messages WHERE id = ?1", params![id])?; .execute("DELETE FROM messages WHERE id = ?1", params![id])?;
Ok(()) Ok(())
} }
/// 清空服务器消息
pub fn clear_server_messages(&self, server_address: &str) -> DatabaseResult<()> { pub fn clear_server_messages(&self, server_address: &str) -> DatabaseResult<()> {
self.connection().execute( self.connection().execute(
"DELETE FROM messages WHERE server_address = ?1", "DELETE FROM messages WHERE server_address = ?1",