diff --git a/bin/ircd/src/server.rs b/bin/ircd/src/server.rs index 851a3a30e..2d1497735 100644 --- a/bin/ircd/src/server.rs +++ b/bin/ircd/src/server.rs @@ -31,9 +31,11 @@ pub struct IrcServerConnection { is_nick_init: bool, is_user_init: bool, is_registered: bool, + is_cap_end: bool, nickname: String, auto_channels: Vec, pub configured_chans: FxHashMap, + capabilities: FxHashMap, // p2p p2p: P2pPtr, senders: SubscriberPtr, @@ -53,6 +55,8 @@ impl IrcServerConnection { senders: SubscriberPtr, subscriber_id: u64, ) -> Self { + let mut capabilities = FxHashMap::default(); + capabilities.insert("no-history".to_string(), false); Self { write_stream, peer_address, @@ -61,9 +65,11 @@ impl IrcServerConnection { is_nick_init: false, is_user_init: false, is_registered: false, + is_cap_end: false, nickname: "anon".to_string(), auto_channels, configured_chans, + capabilities, p2p, senders, subscriber_id, @@ -199,7 +205,78 @@ impl IrcServerConnection { self.on_receive_privmsg(&message, target).await?; } - "CAP" => {} + "CAP" => { + let subcommand = tokens.next().ok_or(Error::MalformedPacket)?.to_uppercase(); + + let capabilities_keys: Vec = self.capabilities.keys().cloned().collect(); + + if subcommand == "LS" { + let cap_ls_reply = format!( + ":{}!anon@dark.fi CAP * LS :{}\r\n", + self.nickname, + capabilities_keys.join(" ") + ); + self.reply(&cap_ls_reply).await?; + } + + if subcommand == "REQ" { + let substr_idx = line.find(':').ok_or(Error::MalformedPacket)?; + + if substr_idx >= line.len() { + return Err(Error::MalformedPacket) + } + + let cap: Vec<&str> = line[substr_idx + 1..].split(" ").collect(); + + let mut ack_list = vec![]; + let mut nak_list = vec![]; + + for c in cap { + if self.capabilities.contains_key(c) { + self.capabilities.insert(c.to_string(), true); + ack_list.push(c); + } else { + nak_list.push(c); + } + } + + let cap_ack_reply = format!( + ":{}!anon@dark.fi CAP * ACK :{}\r\n", + self.nickname, + ack_list.join(" ") + ); + + let cap_nak_reply = format!( + ":{}!anon@dark.fi CAP * NAK :{}\r\n", + self.nickname, + nak_list.join(" ") + ); + + self.reply(&cap_ack_reply).await?; + self.reply(&cap_nak_reply).await?; + } + + if subcommand == "LIST" { + let enabled_capabilities: Vec = self + .capabilities + .clone() + .into_iter() + .filter(|(_, v)| *v) + .map(|(k, _)| k) + .collect(); + + let cap_list_reply = format!( + ":{}!anon@dark.fi CAP * LIST :{}\r\n", + self.nickname, + enabled_capabilities.join(" ") + ); + self.reply(&cap_list_reply).await?; + } + + if subcommand == "END" { + self.is_cap_end = true; + } + } "QUIT" => { // Close the connection return Err(Error::NetworkServiceStopped) @@ -210,7 +287,7 @@ impl IrcServerConnection { } // on registration - if !self.is_registered && self.is_nick_init && self.is_user_init { + if !self.is_registered && self.is_cap_end && self.is_nick_init && self.is_user_init { debug!("Initializing peer connection"); let register_reply = format!(":darkfi 001 {} :Let there be dark\r\n", self.nickname); self.reply(®ister_reply).await?; @@ -221,13 +298,13 @@ impl IrcServerConnection { } // Send dm messages in buffer - for msg in self.privmsgs_buffer.lock().await.to_vec() { - if msg.target == self.nickname || msg.nickname == self.nickname { - self.senders.notify_by_id(msg, self.subscriber_id).await; + if !self.capabilities.get("no-history").unwrap() { + for msg in self.privmsgs_buffer.lock().await.to_vec() { + if msg.target == self.nickname || msg.nickname == self.nickname { + self.senders.notify_by_id(msg, self.subscriber_id).await; + } } } - - // send names command } Ok(()) @@ -282,7 +359,6 @@ impl IrcServerConnection { (*self.seen_msg_ids.lock().await).push(random_id); (*self.privmsgs_buffer.lock().await).push(protocol_msg.clone()) } - self.senders.notify_with_exclude(protocol_msg.clone(), &[self.subscriber_id]).await; debug!(target: "ircd", "PRIVMSG to be sent: {:?}", protocol_msg); @@ -312,9 +388,11 @@ impl IrcServerConnection { } // Send messages in buffer - for msg in self.privmsgs_buffer.lock().await.to_vec() { - if msg.target == chan { - self.senders.notify_by_id(msg, self.subscriber_id).await; + if !self.capabilities.get("no-history").unwrap() { + for msg in self.privmsgs_buffer.lock().await.to_vec() { + if msg.target == chan { + self.senders.notify_by_id(msg, self.subscriber_id).await; + } } } @@ -355,7 +433,10 @@ impl IrcServerConnection { return Ok(()) } - if self.is_nick_init && (self.nickname == msg.target || self.nickname == msg.nickname) { + if self.is_cap_end && + self.is_nick_init && + (self.nickname == msg.target || self.nickname == msg.nickname) + { self.reply(&msg.to_irc_msg()).await?; }