room_daemon/broker/daemon/
config.rs1use std::path::PathBuf;
4
5const UNSAFE_CHARS: &[char] = &['/', '\\', ':', '*', '?', '"', '<', '>', '|', '\0'];
7
8pub(crate) const MAX_ROOM_ID_LEN: usize = 64;
10
11pub fn validate_room_id(room_id: &str) -> Result<(), String> {
16 if room_id.is_empty() {
17 return Err("room ID cannot be empty".into());
18 }
19 if room_id.len() > MAX_ROOM_ID_LEN {
20 return Err(format!(
21 "room ID too long ({} chars, max {MAX_ROOM_ID_LEN})",
22 room_id.len()
23 ));
24 }
25 if room_id == "." || room_id == ".." || room_id.contains("..") {
26 return Err("room ID cannot contain '..'".into());
27 }
28 if room_id.chars().any(|c| c.is_whitespace()) {
29 return Err("room ID cannot contain whitespace".into());
30 }
31 if let Some(bad) = room_id.chars().find(|c| UNSAFE_CHARS.contains(c)) {
32 return Err(format!("room ID contains unsafe character: {bad:?}"));
33 }
34 Ok(())
35}
36
37#[derive(Debug, Clone)]
39pub struct DaemonConfig {
40 pub socket_path: PathBuf,
42 pub data_dir: PathBuf,
45 pub state_dir: PathBuf,
48 pub ws_port: Option<u16>,
50 pub grace_period_secs: u64,
55}
56
57impl DaemonConfig {
58 pub fn chat_path(&self, room_id: &str) -> PathBuf {
60 self.data_dir.join(format!("{room_id}.chat"))
61 }
62
63 pub fn token_map_path(&self, room_id: &str) -> PathBuf {
65 crate::paths::broker_tokens_path(&self.state_dir, room_id)
66 }
67
68 pub fn system_tokens_path(&self) -> PathBuf {
74 self.state_dir.join("tokens.json")
75 }
76
77 pub fn subscription_map_path(&self, room_id: &str) -> PathBuf {
79 crate::paths::broker_subscriptions_path(&self.state_dir, room_id)
80 }
81
82 pub fn event_filter_map_path(&self, room_id: &str) -> PathBuf {
84 crate::paths::broker_event_filters_path(&self.state_dir, room_id)
85 }
86}
87
88impl Default for DaemonConfig {
89 fn default() -> Self {
90 Self {
91 socket_path: crate::paths::room_socket_path(),
92 data_dir: crate::paths::room_data_dir(),
93 state_dir: crate::paths::room_state_dir(),
94 ws_port: None,
95 grace_period_secs: 30,
96 }
97 }
98}