1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::protocol::{Message, Reply};
use crate::state::ServerState;
pub async fn handle_names(
server_state: Arc<RwLock<ServerState>>,
connection_id: u64,
params: Vec<String>,
) -> Result<Vec<Message>, Box<dyn std::error::Error>> {
let state = server_state.read().await;
let mut messages = Vec::new();
// Get the requesting connection
let requesting_connection = state.connections.get(&connection_id);
if requesting_connection.is_none() {
return Ok(messages);
}
let req_conn = requesting_connection.unwrap();
let requester_nick = req_conn.nickname.as_deref().unwrap_or("*");
// Parse parameters - NAMES [channels] [target]
let channels_to_list = if params.is_empty() {
// If no parameters, list names for all channels the user is in
let mut user_channels = Vec::new();
for channel_entry in state.channels.iter() {
let (channel_name, channel) = channel_entry.pair();
if channel.is_member(connection_id) {
user_channels.push(channel_name.clone());
}
}
user_channels
} else {
// Split comma-separated channel list
params[0].split(',').map(|s| s.to_string()).collect()
};
// Generate NAMES reply for each channel
for channel_name in &channels_to_list {
if let Some(channel) = state.channels.get(channel_name) {
// Check if user can see channel names
let can_see_names = channel.is_member(connection_id) ||
(!channel.modes.contains(&'s') && !channel.modes.contains(&'p'));
if can_see_names {
let mut names = Vec::new();
// Collect member names with status prefixes
for member_entry in channel.members.iter() {
let member_id = *member_entry.key();
if let Some(member_conn) = state.connections.get(&member_id) {
if let Some(member_nick) = &member_conn.nickname {
let mut name_with_prefix = String::new();
// Add status prefixes (highest status first)
if member_entry.value().modes.contains(&'o') {
name_with_prefix.push('@');
} else if member_entry.value().modes.contains(&'v') {
name_with_prefix.push('+');
}
name_with_prefix.push_str(member_nick);
names.push(name_with_prefix);
}
}
}
// Determine channel symbol (= public, @ secret, * private)
let symbol = if channel.modes.contains(&'s') {
'@' // Secret channel
} else if channel.modes.contains(&'p') {
'*' // Private channel
} else {
'=' // Public channel
};
// Send NAMES reply
messages.push(Message::from(Reply::NamReply {
nick: requester_nick.to_string(),
symbol,
channel: channel_name.clone(),
names,
}));
}
}
// Always send END OF NAMES, even for non-existent channels
messages.push(Message::from(Reply::EndOfNames {
nick: requester_nick.to_string(),
channel: channel_name.clone(),
}));
}
// If no channels were specified and user is not in any channels,
// still need to send end of names for the implicit query
if params.is_empty() && channels_to_list.is_empty() {
messages.push(Message::from(Reply::EndOfNames {
nick: requester_nick.to_string(),
channel: "*".to_string(),
}));
}
Ok(messages)
}