command.rs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. //! IRC command implemenatations
  19. //!
  20. //! These try to follow the RFCs, modified in order for our P2P stack.
  21. //! Copied from <https://simple.wikipedia.org/wiki/List_of_Internet_Relay_Chat_commands>
  22. //!
  23. //! Unimplemented commands:
  24. //! * `AWAY`
  25. //! * `CONNECT`
  26. //! * `DIE`
  27. //! * `ERROR`
  28. //! * `INVITE`
  29. //! * `ISON`
  30. //! * `KICK`
  31. //! * `KILL`
  32. //! * `NOTICE`
  33. //! * `OPER`
  34. //! * `PASS`
  35. //! * `RESTART`
  36. //! * `SERVICE`
  37. //! * `SERVLIST`
  38. //! * `SERVER`
  39. //! * `SQUERY`
  40. //! * `SQUIT`
  41. //! * `SUMMON`
  42. //! * `TRACE`
  43. //! * `USERHOST`
  44. //! * `WALLOPS`
  45. //! * `WHO`
  46. //! * `WHOIS`
  47. //! * `WHOWAS`
  48. //!
  49. //! Some of the above commands could actually be implemented and could
  50. //! work in respect to the P2P network.
  51. use std::{collections::HashSet, sync::atomic::Ordering::SeqCst};
  52. use darkfi::Result;
  53. use darkfi_serial::deserialize_async_partial;
  54. use log::{error, info};
  55. use super::{
  56. client::{Client, ReplyType},
  57. rpl::*,
  58. server::MAX_NICK_LEN,
  59. IrcChannel, SERVER_NAME,
  60. };
  61. impl Client {
  62. /// `ADMIN [<server>]`
  63. ///
  64. /// Asks the server for information about the administrator of the server.
  65. pub async fn handle_cmd_admin(&self, _args: &str) -> Result<Vec<ReplyType>> {
  66. if !self.registered.load(SeqCst) {
  67. self.penalty.fetch_add(1, SeqCst);
  68. return Ok(vec![ReplyType::Server((
  69. ERR_NOTREGISTERED,
  70. format!("* :{}", NOT_REGISTERED),
  71. ))])
  72. }
  73. let nick = self.nickname.read().await.to_string();
  74. let replies = vec![
  75. ReplyType::Server((
  76. RPL_ADMINME,
  77. format!("{} {} :Administrative info", nick, SERVER_NAME),
  78. )),
  79. ReplyType::Server((RPL_ADMINLOC1, format!("{} :", nick))),
  80. ReplyType::Server((RPL_ADMINLOC2, format!("{} :", nick))),
  81. ReplyType::Server((RPL_ADMINEMAIL, format!("{} :anon@darkirc", nick))),
  82. ];
  83. Ok(replies)
  84. }
  85. /// `CAP <args>`
  86. pub async fn handle_cmd_cap(&self, args: &str) -> Result<Vec<ReplyType>> {
  87. let mut tokens = args.split_ascii_whitespace();
  88. let Some(subcommand) = tokens.next() else {
  89. self.penalty.fetch_add(1, SeqCst);
  90. return Ok(vec![ReplyType::Server((
  91. ERR_NEEDMOREPARAMS,
  92. format!("{} CAP :{}", self.nickname.read().await, INVALID_SYNTAX),
  93. ))])
  94. };
  95. let caps_keys: Vec<String> = self.caps.read().await.keys().cloned().collect();
  96. let nick = self.nickname.read().await.to_string();
  97. match subcommand.to_uppercase().as_str() {
  98. "LS" => {
  99. /*
  100. let Some(_version) = tokens.next() else {
  101. return Ok(vec![ReplyType::Server((
  102. ERR_NEEDMOREPARAMS,
  103. format!("{} CAP :{}", self.nickname.read().await, INVALID_SYNTAX),
  104. ))])
  105. };
  106. */
  107. self.reg_paused.store(true, SeqCst);
  108. return Ok(vec![ReplyType::Cap(format!("CAP * LS :{}", caps_keys.join(" ")))])
  109. }
  110. "REQ" => {
  111. let Some(substr_idx) = args.find(':') else {
  112. return Ok(vec![ReplyType::Server((
  113. ERR_NEEDMOREPARAMS,
  114. format!("{} CAP :{}", nick, INVALID_SYNTAX),
  115. ))])
  116. };
  117. if substr_idx >= args.len() {
  118. return Ok(vec![ReplyType::Server((
  119. ERR_NEEDMOREPARAMS,
  120. format!("{} CAP :{}", nick, INVALID_SYNTAX),
  121. ))])
  122. }
  123. let cap_reqs: Vec<&str> = args[substr_idx + 1..].split(' ').collect();
  124. let mut ack_list = vec![];
  125. let mut nak_list = vec![];
  126. let mut available_caps = self.caps.write().await;
  127. for cap in cap_reqs {
  128. if available_caps.contains_key(cap) {
  129. available_caps.insert(cap.to_string(), true);
  130. ack_list.push(cap);
  131. } else {
  132. nak_list.push(cap);
  133. }
  134. }
  135. let mut replies = vec![];
  136. if !ack_list.is_empty() {
  137. replies.push(ReplyType::Cap(format!(
  138. "CAP {} ACK :{}",
  139. nick,
  140. ack_list.join(" ")
  141. )));
  142. }
  143. if !nak_list.is_empty() {
  144. replies.push(ReplyType::Cap(format!(
  145. "CAP {} NAK :{}",
  146. nick,
  147. nak_list.join(" ")
  148. )));
  149. }
  150. return Ok(replies)
  151. }
  152. "LIST" => {
  153. let enabled_caps: Vec<String> = self
  154. .caps
  155. .read()
  156. .await
  157. .clone()
  158. .into_iter()
  159. .filter(|(_, v)| *v)
  160. .map(|(k, _)| k)
  161. .collect();
  162. return Ok(vec![ReplyType::Cap(format!(
  163. "CAP {} LIST :{}",
  164. nick,
  165. enabled_caps.join(" ")
  166. ))])
  167. }
  168. "END" => {
  169. // At CAP END, if we have USER and NICK, we can welcome them.
  170. self.reg_paused.store(false, SeqCst);
  171. if self.registered.load(SeqCst) && !self.is_cap_end.load(SeqCst) {
  172. self.is_cap_end.store(true, SeqCst);
  173. return Ok(self.welcome().await)
  174. }
  175. return Ok(vec![])
  176. }
  177. _ => {}
  178. }
  179. self.penalty.fetch_add(1, SeqCst);
  180. Ok(vec![ReplyType::Server((
  181. ERR_NEEDMOREPARAMS,
  182. format!("{} CAP :{}", nick, INVALID_SYNTAX),
  183. ))])
  184. }
  185. /// `INFO [<target>]`
  186. ///
  187. /// Gives information about the `<target>` server, or the current server if
  188. /// `<target>` is not used. The information includes the server's version,
  189. /// when it was compiled, the patch level, when it was started, and any
  190. /// other information which might be relevant.
  191. pub async fn handle_cmd_info(&self, _args: &str) -> Result<Vec<ReplyType>> {
  192. if !self.registered.load(SeqCst) {
  193. self.penalty.fetch_add(1, SeqCst);
  194. return Ok(vec![ReplyType::Server((
  195. ERR_NOTREGISTERED,
  196. format!("* :{}", NOT_REGISTERED),
  197. ))])
  198. }
  199. let nick = self.nickname.read().await.clone();
  200. let replies = vec![
  201. ReplyType::Server((
  202. RPL_INFO,
  203. format!("{} :DarkIRC {}", nick, env!("CARGO_PKG_VERSION")),
  204. )),
  205. ReplyType::Server((RPL_ENDOFINFO, format!("{} :End of INFO list", nick))),
  206. ];
  207. Ok(replies)
  208. }
  209. /// `JOIN <channels> [<keys>]`
  210. ///
  211. /// Makes the client join the channels in the list `<channels>`.
  212. /// Passwords can be used in the list `<keys>`. If the channels do not
  213. /// exist, they will be created.
  214. pub async fn handle_cmd_join(&self, args: &str) -> Result<Vec<ReplyType>> {
  215. if !self.registered.load(SeqCst) {
  216. self.penalty.fetch_add(1, SeqCst);
  217. return Ok(vec![ReplyType::Server((
  218. ERR_NOTREGISTERED,
  219. format!("* :{}", NOT_REGISTERED),
  220. ))])
  221. }
  222. // Client's (already) active channels
  223. let mut active_channels = self.channels.write().await;
  224. // Here we'll hold valid channel names.
  225. let mut channels = HashSet::new();
  226. // Let's scan through our channels. For now we'll only support
  227. // channel names starting with a single '#' character.
  228. let nick = self.nickname.read().await.to_string();
  229. let tokens = args.split_ascii_whitespace();
  230. for channel in tokens {
  231. if !channel.starts_with('#') {
  232. self.penalty.fetch_add(1, SeqCst);
  233. return Ok(vec![ReplyType::Server((
  234. ERR_NEEDMOREPARAMS,
  235. format!("{} JOIN :{}", nick, INVALID_SYNTAX),
  236. ))])
  237. }
  238. if !active_channels.contains(channel) {
  239. channels.insert(channel.to_string());
  240. }
  241. }
  242. // We need at least one channel.
  243. if channels.is_empty() {
  244. self.penalty.fetch_add(1, SeqCst);
  245. return Ok(vec![ReplyType::Server((
  246. ERR_NEEDMOREPARAMS,
  247. format!("{} JOIN :{}", nick, INVALID_SYNTAX),
  248. ))])
  249. }
  250. // Weechat sends channels as `#chan1,#chan2,#chan3`. Handle it.
  251. if channels.len() == 1 {
  252. let list = channels.iter().next().unwrap().clone();
  253. channels.remove(list.as_str());
  254. for channel in list.split(',') {
  255. if !channel.starts_with('#') || channel.as_bytes().len() > MAX_NICK_LEN {
  256. self.penalty.fetch_add(1, SeqCst);
  257. return Ok(vec![ReplyType::Server((
  258. ERR_NEEDMOREPARAMS,
  259. format!("{} JOIN :{}", nick, INVALID_SYNTAX),
  260. ))])
  261. }
  262. channels.insert(channel.to_string());
  263. }
  264. }
  265. // Create new channels for this client and construct replies.
  266. let mut server_channels = self.server.channels.write().await;
  267. let mut replies = vec![];
  268. for channel in channels.iter() {
  269. // Insert the channel name into the set of client's active channels
  270. active_channels.insert(channel.clone());
  271. // Create or update the channel on the server side.
  272. if let Some(server_chan) = server_channels.get_mut(channel) {
  273. server_chan.nicks.insert(nick.clone());
  274. } else {
  275. let chan = IrcChannel {
  276. topic: String::new(),
  277. nicks: HashSet::from([nick.clone()]),
  278. saltbox: None,
  279. };
  280. server_channels.insert(channel.clone(), chan);
  281. }
  282. // Create the replies
  283. replies.push(ReplyType::Client((nick.clone(), format!("JOIN :{}", channel))));
  284. replies.push(ReplyType::Server((
  285. RPL_NAMREPLY,
  286. format!("{} = {} :{}", nick, channel, nick),
  287. )));
  288. replies.push(ReplyType::Server((
  289. RPL_ENDOFNAMES,
  290. format!("{} {} :End of NAMES list", nick, channel),
  291. )));
  292. if let Some(chan) = server_channels.get(channel) {
  293. if !chan.topic.is_empty() {
  294. replies.push(ReplyType::Client((
  295. nick.clone(),
  296. format!("TOPIC {} :{}", channel, chan.topic),
  297. )));
  298. }
  299. }
  300. }
  301. // Drop the locks as they're used in get_history()
  302. drop(active_channels);
  303. drop(server_channels);
  304. // Potentially extend the replies with channel history
  305. replies.append(&mut self.get_history(&channels).await.unwrap());
  306. Ok(replies)
  307. }
  308. /// `LIST [<channels> [<server>]]`
  309. ///
  310. /// List all channels on the server. If the list `<channels>` is given, it
  311. /// will return the channel topics. If `<server>` is given, the command will
  312. /// be sent to `<server>` for evaluation.
  313. pub async fn handle_cmd_list(&self, _args: &str) -> Result<Vec<ReplyType>> {
  314. if !self.registered.load(SeqCst) {
  315. self.penalty.fetch_add(1, SeqCst);
  316. return Ok(vec![ReplyType::Server((
  317. ERR_NOTREGISTERED,
  318. format!("* :{}", NOT_REGISTERED),
  319. ))])
  320. }
  321. let nick = self.nickname.read().await.to_string();
  322. let mut list = vec![];
  323. for (name, channel) in self.server.channels.read().await.iter() {
  324. list.push(format!("{} {} {} :{}", nick, name, channel.nicks.len(), channel.topic));
  325. }
  326. let mut replies = vec![];
  327. replies.push(ReplyType::Server((RPL_LISTSTART, format!("{} Channel :Users Name", nick))));
  328. for chan in list {
  329. replies.push(ReplyType::Server((RPL_LIST, chan)));
  330. }
  331. replies.push(ReplyType::Server((RPL_LISTEND, format!("{} :End of /LIST", nick))));
  332. Ok(replies)
  333. }
  334. /// `MODE <nickname> <flags>`
  335. /// `MODE <channel> <flags>`
  336. ///
  337. /// The MODE command has two uses. It can be used to set both user and
  338. /// channel modes.
  339. pub async fn handle_cmd_mode(&self, args: &str) -> Result<Vec<ReplyType>> {
  340. if !self.registered.load(SeqCst) {
  341. self.penalty.fetch_add(1, SeqCst);
  342. return Ok(vec![ReplyType::Server((
  343. ERR_NOTREGISTERED,
  344. format!("* :{}", NOT_REGISTERED),
  345. ))])
  346. }
  347. let nick = self.nickname.read().await.to_string();
  348. let mut tokens = args.split_ascii_whitespace();
  349. let Some(target) = tokens.next() else {
  350. self.penalty.fetch_add(1, SeqCst);
  351. return Ok(vec![ReplyType::Server((
  352. ERR_NEEDMOREPARAMS,
  353. format!("{} MODE :{}", nick, INVALID_SYNTAX),
  354. ))])
  355. };
  356. if target == nick {
  357. return Ok(vec![ReplyType::Server((RPL_UMODEIS, format!("{} +", nick)))])
  358. }
  359. if !target.starts_with('#') {
  360. return Ok(vec![ReplyType::Server((
  361. ERR_USERSDONTMATCH,
  362. format!("{} :Can't set/get mode for other users", nick),
  363. ))])
  364. }
  365. if !self.server.channels.read().await.contains_key(target) {
  366. return Ok(vec![ReplyType::Server((
  367. ERR_NOSUCHNICK,
  368. format!("{} {} :No such nick or channel name", nick, target),
  369. ))])
  370. }
  371. Ok(vec![ReplyType::Server((RPL_CHANNELMODEIS, format!("{} {} +", nick, target)))])
  372. }
  373. /// `MOTD [<server>]`
  374. ///
  375. /// Returns the message of the day on `<server>` or the current server if
  376. /// it is not stated.
  377. pub async fn handle_cmd_motd(&self, _args: &str) -> Result<Vec<ReplyType>> {
  378. let nick = self.nickname.read().await.to_string();
  379. Ok(vec![
  380. ReplyType::Server((
  381. RPL_MOTDSTART,
  382. format!("{} :- {} message of the day", nick, SERVER_NAME),
  383. )),
  384. ReplyType::Server((RPL_MOTD, format!("{} :Let there be dark!", nick))),
  385. ReplyType::Server((RPL_ENDOFMOTD, format!("{} :End of /MOTD command.", nick))),
  386. ])
  387. }
  388. /// `NAMES [<channel>]`
  389. ///
  390. /// Returns a list of who is on the list of `<channel>`, by channel name.
  391. /// If `<channel>` is not used, all users are shown. They are grouped by
  392. /// channel name with all users who are not on a channel being shown as
  393. /// part of channel "*".
  394. pub async fn handle_cmd_names(&self, args: &str) -> Result<Vec<ReplyType>> {
  395. if !self.registered.load(SeqCst) {
  396. self.penalty.fetch_add(1, SeqCst);
  397. return Ok(vec![ReplyType::Server((
  398. ERR_NOTREGISTERED,
  399. format!("* :{}", NOT_REGISTERED),
  400. ))])
  401. }
  402. let nick = self.nickname.read().await.to_string();
  403. let mut tokens = args.split_ascii_whitespace();
  404. let mut replies = vec![];
  405. // If a channel was requested, reply only with that one.
  406. // Otherwise, return info for all known channels.
  407. if let Some(req_chan) = tokens.next() {
  408. if let Some(chan) = self.server.channels.read().await.get(req_chan) {
  409. let nicks: Vec<String> = chan.nicks.iter().cloned().collect();
  410. replies.push(ReplyType::Server((
  411. RPL_NAMREPLY,
  412. format!("{} = {} :{}", nick, req_chan, nicks.join(" ")),
  413. )));
  414. }
  415. replies.push(ReplyType::Server((
  416. RPL_ENDOFNAMES,
  417. format!("{} {} :End of NAMES list", nick, req_chan),
  418. )));
  419. Ok(replies)
  420. } else {
  421. for (name, chan) in self.server.channels.read().await.iter() {
  422. let nicks: Vec<String> = chan.nicks.iter().cloned().collect();
  423. replies.push(ReplyType::Server((
  424. RPL_NAMREPLY,
  425. format!("{} = {} :{}", nick, name, nicks.join(" ")),
  426. )));
  427. }
  428. replies.push(ReplyType::Server((
  429. RPL_ENDOFNAMES,
  430. format!("{} * :End of NAMES list", nick),
  431. )));
  432. Ok(replies)
  433. }
  434. }
  435. /// `NICK <nickname>`
  436. ///
  437. /// Allows a client to change their IRC nickname.
  438. pub async fn handle_cmd_nick(&self, args: &str) -> Result<Vec<ReplyType>> {
  439. // Parse the line
  440. let mut tokens = args.split_ascii_whitespace();
  441. // Reference the current nickname
  442. let old_nick = self.nickname.read().await.to_string();
  443. let Some(nickname) = tokens.next() else {
  444. self.penalty.fetch_add(1, SeqCst);
  445. return Ok(vec![ReplyType::Server((
  446. ERR_NEEDMOREPARAMS,
  447. format!("{} NICK :{}", old_nick, INVALID_SYNTAX),
  448. ))])
  449. };
  450. // Forbid disallowed characters.
  451. // The next() call is done to check for ASCII whitespace in the nick.
  452. if tokens.next().is_some() || nickname.starts_with(':') || nickname.starts_with('#') {
  453. self.penalty.fetch_add(1, SeqCst);
  454. return Ok(vec![ReplyType::Server((
  455. ERR_ERRONEOUSNICKNAME,
  456. format!("{} {} :Erroneous nickname", old_nick, nickname),
  457. ))])
  458. }
  459. // Disallow too long nicks
  460. if nickname.as_bytes().len() > MAX_NICK_LEN {
  461. self.penalty.fetch_add(1, SeqCst);
  462. return Ok(vec![ReplyType::Server((
  463. ERR_ERRONEOUSNICKNAME,
  464. format!("{} {} :Nickname too long", old_nick, nickname),
  465. ))])
  466. }
  467. // Set the new nickname
  468. *self.nickname.write().await = nickname.to_string();
  469. // If the username is set, we can complete the registration
  470. if *self.username.read().await != "*" && !self.registered.load(SeqCst) {
  471. self.registered.store(true, SeqCst);
  472. if self.reg_paused.load(SeqCst) {
  473. return Ok(vec![])
  474. } else {
  475. return Ok(self.welcome().await)
  476. }
  477. }
  478. // If we were registered, we send a client reply about it.
  479. if self.registered.load(SeqCst) {
  480. Ok(vec![ReplyType::Client((old_nick, format!("NICK :{}", nickname)))])
  481. } else {
  482. // Otherwise, we don't reply.
  483. Ok(vec![])
  484. }
  485. }
  486. /// `PART <channel>`
  487. ///
  488. /// Causes a user to leave the channel `<channel>`.
  489. pub async fn handle_cmd_part(&self, args: &str) -> Result<Vec<ReplyType>> {
  490. if !self.registered.load(SeqCst) {
  491. self.penalty.fetch_add(1, SeqCst);
  492. return Ok(vec![ReplyType::Server((
  493. ERR_NOTREGISTERED,
  494. format!("* :{}", NOT_REGISTERED),
  495. ))])
  496. }
  497. let nick = self.nickname.read().await.to_string();
  498. let mut tokens = args.split_ascii_whitespace();
  499. let Some(channel) = tokens.next() else {
  500. self.penalty.fetch_add(1, SeqCst);
  501. return Ok(vec![ReplyType::Server((
  502. ERR_NEEDMOREPARAMS,
  503. format!("{} PART :{}", nick, INVALID_SYNTAX),
  504. ))])
  505. };
  506. if !channel.starts_with('#') {
  507. self.penalty.fetch_add(1, SeqCst);
  508. return Ok(vec![ReplyType::Server((
  509. ERR_NEEDMOREPARAMS,
  510. format!("{} PART :{}", nick, INVALID_SYNTAX),
  511. ))])
  512. }
  513. let mut active_channels = self.channels.write().await;
  514. if !active_channels.contains(channel) {
  515. return Ok(vec![ReplyType::Server((
  516. ERR_NOSUCHCHANNEL,
  517. format!("{} {} :No such channel", nick, channel),
  518. ))])
  519. }
  520. // Remove the channel from the client's channel list
  521. active_channels.remove(channel);
  522. let replies = vec![ReplyType::Client((nick, format!("PART {} :Bye", channel)))];
  523. Ok(replies)
  524. }
  525. /// `PING <server1>`
  526. ///
  527. /// Tests a connection. A PING message results in a PONG reply.
  528. pub async fn handle_cmd_ping(&self, args: &str) -> Result<Vec<ReplyType>> {
  529. if !self.registered.load(SeqCst) {
  530. self.penalty.fetch_add(1, SeqCst);
  531. return Ok(vec![ReplyType::Server((
  532. ERR_NOTREGISTERED,
  533. format!("* :{}", NOT_REGISTERED),
  534. ))])
  535. }
  536. let mut tokens = args.split_ascii_whitespace();
  537. let Some(origin) = tokens.next() else {
  538. self.penalty.fetch_add(1, SeqCst);
  539. return Ok(vec![ReplyType::Server((
  540. ERR_NOORIGIN,
  541. format!("{} :No origin specified", self.nickname.read().await),
  542. ))])
  543. };
  544. Ok(vec![ReplyType::Pong(origin.to_string())])
  545. }
  546. /// `PRIVMSG <msgtarget> <message>`
  547. ///
  548. /// Sends `<message>` to `<msgtarget>`. The target is usually a user or
  549. /// a channel.
  550. pub async fn handle_cmd_privmsg(&self, args: &str) -> Result<Vec<ReplyType>> {
  551. if !self.registered.load(SeqCst) {
  552. self.penalty.fetch_add(1, SeqCst);
  553. return Ok(vec![ReplyType::Server((
  554. ERR_NOTREGISTERED,
  555. format!("* :{}", NOT_REGISTERED),
  556. ))])
  557. }
  558. let nick = self.nickname.read().await.to_string();
  559. let mut tokens = args.split_ascii_whitespace();
  560. let Some(target) = tokens.next() else {
  561. return Ok(vec![ReplyType::Server((
  562. ERR_NORECIPIENT,
  563. format!("{} :No recipient given (PRIVMSG)", nick),
  564. ))])
  565. };
  566. let Some(message) = tokens.next() else {
  567. return Ok(vec![ReplyType::Server((
  568. ERR_NOTEXTTOSEND,
  569. format!("{} :No text to send", nick),
  570. ))])
  571. };
  572. if !message.starts_with(':') {
  573. return Ok(vec![ReplyType::Server((
  574. ERR_NOTEXTTOSEND,
  575. format!("{} :No text to send", nick),
  576. ))])
  577. }
  578. // We only send a client reply if the message is for ourself or if
  579. // we're trying to communicate with IRC services.
  580. // Anything else is rendered by the IRC client and not supposed
  581. // to be echoed by the IRC serer.
  582. if target == nick {
  583. return Ok(vec![ReplyType::Client((
  584. target.to_string(),
  585. format!("PRIVMSG {} {}", target, message),
  586. ))])
  587. }
  588. // Handle queries to NickServ
  589. if target.to_lowercase().as_str() == "nickserv" {
  590. return self.nickserv.handle_query(message.strip_prefix(':').unwrap()).await
  591. }
  592. // If it's a DM and we don't have an encryption key, we will
  593. // refuse to send it. Send ERR_NORECIPIENT to the client.
  594. if !target.starts_with('#') && !self.server.contacts.read().await.contains_key(target) {
  595. return Ok(vec![ReplyType::Server((ERR_NOSUCHNICK, format!("{} :{}", nick, target)))])
  596. }
  597. Ok(vec![])
  598. }
  599. /// `REHASH`
  600. ///
  601. /// Causes the server to re-read and re-process its configuration file(s).
  602. pub async fn handle_cmd_rehash(&self, _args: &str) -> Result<Vec<ReplyType>> {
  603. info!("Attempting to rehash server...");
  604. if let Err(e) = self.server.rehash().await {
  605. error!("Failed to rehash server: {}", e);
  606. }
  607. Ok(vec![])
  608. }
  609. /// `TOPIC <channel> [<topic>]`
  610. ///
  611. /// Used to get the channel topic on `<channel>`. If `<topic>` is given, it
  612. /// sets the channel topic to `<topic>`.
  613. pub async fn handle_cmd_topic(&self, args: &str) -> Result<Vec<ReplyType>> {
  614. if !self.registered.load(SeqCst) {
  615. self.penalty.fetch_add(1, SeqCst);
  616. return Ok(vec![ReplyType::Server((
  617. ERR_NOTREGISTERED,
  618. format!("* :{}", NOT_REGISTERED),
  619. ))])
  620. }
  621. let nick = self.nickname.read().await.to_string();
  622. let mut tokens = args.split_ascii_whitespace();
  623. let Some(channel) = tokens.next() else {
  624. self.penalty.fetch_add(1, SeqCst);
  625. return Ok(vec![ReplyType::Server((
  626. ERR_NEEDMOREPARAMS,
  627. format!("{} TOPIC :{}", nick, INVALID_SYNTAX),
  628. ))])
  629. };
  630. if !self.server.channels.read().await.contains_key(channel) {
  631. return Ok(vec![ReplyType::Server((
  632. ERR_NOSUCHCHANNEL,
  633. format!("{} {} :No such channel", nick, channel),
  634. ))])
  635. }
  636. // If there's a topic, we'll set it, otherwise return the set topic.
  637. let Some(topic) = tokens.next() else {
  638. let topic = self.server.channels.read().await.get(channel).unwrap().topic.clone();
  639. if topic.is_empty() {
  640. return Ok(vec![ReplyType::Server((
  641. RPL_NOTOPIC,
  642. format!("{} {} :No topic is set", nick, channel),
  643. ))])
  644. } else {
  645. return Ok(vec![ReplyType::Server((
  646. RPL_TOPIC,
  647. format!("{} {} :{}", nick, channel, topic),
  648. ))])
  649. }
  650. };
  651. // Set the new topic
  652. self.server.channels.write().await.get_mut(channel).unwrap().topic =
  653. topic.strip_prefix(':').unwrap().to_string();
  654. // Send reply
  655. let replies = vec![ReplyType::Client((nick, format!("TOPIC {} {}", channel, topic)))];
  656. Ok(replies)
  657. }
  658. /// `USER <user> <mode> <unused> <realname>`
  659. ///
  660. /// This command is used at the beginning of a connection to specify the
  661. /// username, hostname, real name, and the initial user modes of the
  662. /// connecting client. `<realname>` may contain spaces, and thus must be
  663. /// prefixed with a colon.
  664. pub async fn handle_cmd_user(&self, args: &str) -> Result<Vec<ReplyType>> {
  665. if self.registered.load(SeqCst) {
  666. self.penalty.fetch_add(1, SeqCst);
  667. return Ok(vec![ReplyType::Server((
  668. ERR_ALREADYREGISTERED,
  669. format!("{} :{}", self.nickname.read().await, ALREADY_REGISTERED),
  670. ))])
  671. }
  672. // Parse the line
  673. let nick = self.nickname.read().await.to_string();
  674. let mut tokens = args.split_ascii_whitespace();
  675. let Some(username) = tokens.next() else {
  676. self.penalty.fetch_add(1, SeqCst);
  677. return Ok(vec![ReplyType::Server((
  678. ERR_NEEDMOREPARAMS,
  679. format!("{} USER :{}", nick, INVALID_SYNTAX),
  680. ))])
  681. };
  682. // Mode syntax is currently ignored, but should be part of the command
  683. let Some(_mode) = tokens.next() else {
  684. self.penalty.fetch_add(1, SeqCst);
  685. return Ok(vec![ReplyType::Server((
  686. ERR_NEEDMOREPARAMS,
  687. format!("{} USER :{}", nick, INVALID_SYNTAX),
  688. ))])
  689. };
  690. // Next token is unused per RFC, but should be part of the command
  691. let Some(_unused) = tokens.next() else {
  692. self.penalty.fetch_add(1, SeqCst);
  693. return Ok(vec![ReplyType::Server((
  694. ERR_NEEDMOREPARAMS,
  695. format!("{} USER :{}", nick, INVALID_SYNTAX),
  696. ))])
  697. };
  698. // The final token should be realname and should start with a colon
  699. let Some(realname) = tokens.next() else {
  700. self.penalty.fetch_add(1, SeqCst);
  701. return Ok(vec![ReplyType::Server((
  702. ERR_NEEDMOREPARAMS,
  703. format!("{} USER :{}", nick, INVALID_SYNTAX),
  704. ))])
  705. };
  706. if !realname.starts_with(':') {
  707. self.penalty.fetch_add(1, SeqCst);
  708. return Ok(vec![ReplyType::Server((
  709. ERR_NEEDMOREPARAMS,
  710. format!("{} USER :{}", nick, INVALID_SYNTAX),
  711. ))])
  712. }
  713. *self.username.write().await = username.to_string();
  714. *self.realname.write().await = realname.to_string();
  715. // If the nickname is set, we can complete the registration
  716. if nick != "*" {
  717. self.registered.store(true, SeqCst);
  718. if self.reg_paused.load(SeqCst) {
  719. return Ok(vec![])
  720. } else {
  721. return Ok(self.welcome().await)
  722. }
  723. }
  724. // Otherwise, we don't have to reply.
  725. Ok(vec![])
  726. }
  727. /// `VERSION`
  728. ///
  729. /// Returns the version of the server.
  730. pub async fn handle_cmd_version(&self, _args: &str) -> Result<Vec<ReplyType>> {
  731. if !self.registered.load(SeqCst) {
  732. self.penalty.fetch_add(1, SeqCst);
  733. return Ok(vec![ReplyType::Server((
  734. ERR_NOTREGISTERED,
  735. format!("* :{}", NOT_REGISTERED),
  736. ))])
  737. }
  738. let replies = vec![ReplyType::Server((
  739. RPL_VERSION,
  740. format!(
  741. "{} {} {} :Let there be dark!",
  742. self.nickname.read().await,
  743. env!("CARGO_PKG_VERSION"),
  744. SERVER_NAME
  745. ),
  746. ))];
  747. Ok(replies)
  748. }
  749. /// Internal function that constructs the welcome message.
  750. async fn welcome(&self) -> Vec<ReplyType> {
  751. let nick = self.nickname.read().await.to_string();
  752. let mut replies = vec![
  753. ReplyType::Server((RPL_WELCOME, format!("{} :{}", nick, WELCOME))),
  754. ReplyType::Server((
  755. RPL_YOURHOST,
  756. format!(
  757. "{} :Your host is irc.dark.fi, running version {}",
  758. nick,
  759. env!("CARGO_PKG_VERSION")
  760. ),
  761. )),
  762. ];
  763. // Append the MOTD
  764. replies.append(&mut self.handle_cmd_motd("").await.unwrap());
  765. // If we have any configured autojoin channels, let's join the user
  766. // and set their topics, if any.
  767. let mut config_chans = self.server.channels.write().await;
  768. let mut autojoin_chans = HashSet::new();
  769. for channel in self.server.autojoin.read().await.iter() {
  770. autojoin_chans.insert(channel.clone());
  771. }
  772. for channel in autojoin_chans.iter() {
  773. replies.push(ReplyType::Client((nick.clone(), format!("JOIN :{}", channel))));
  774. replies.push(ReplyType::Server((
  775. RPL_NAMREPLY,
  776. format!("{} = {} :{}", nick, channel, nick),
  777. )));
  778. replies.push(ReplyType::Server((
  779. RPL_ENDOFNAMES,
  780. format!("{} {} :End of NAMES list", nick, channel),
  781. )));
  782. if let Some(chan) = config_chans.get_mut(channel) {
  783. if !chan.topic.is_empty() {
  784. replies.push(ReplyType::Client((
  785. nick.clone(),
  786. format!("TOPIC {} :{}", channel, chan.topic),
  787. )));
  788. }
  789. // Insert the client into the channel nicklist
  790. chan.nicks.insert(nick.clone());
  791. }
  792. }
  793. // Drop the write lock, it's used in get_history()
  794. drop(config_chans);
  795. // Potentially extend replies with history
  796. autojoin_chans.insert(self.nickname.read().await.to_string());
  797. replies.append(&mut self.get_history(&autojoin_chans).await.unwrap());
  798. replies
  799. }
  800. /// Internal function that scans the DAG and returns events for
  801. /// given channels. Will return empty if no_history CAP is requested.
  802. async fn get_history(&self, channels: &HashSet<String>) -> Result<Vec<ReplyType>> {
  803. if channels.is_empty() || *self.caps.read().await.get("no-history").unwrap() {
  804. return Ok(vec![])
  805. }
  806. // Fetch and order all the events from the DAG
  807. let dag_events = self.server.darkirc.event_graph.order_events().await;
  808. // Here we'll hold the events in order we'll push to the client
  809. let mut replies = vec![];
  810. for event_id in dag_events.iter() {
  811. // If it was seen, skip
  812. match self.is_seen(event_id).await {
  813. Ok(true) => continue,
  814. Ok(false) => {}
  815. Err(e) => {
  816. error!("[IRC CLIENT] (get_history) self.is_seen({}) failed: {}", event_id, e);
  817. return Err(e)
  818. }
  819. }
  820. // Get the event from the DAG
  821. let event = self.server.darkirc.event_graph.dag_get(event_id).await.unwrap().unwrap();
  822. // Try to deserialize it. (Here we skip errors)
  823. let Ok((mut privmsg, _)) = deserialize_async_partial(event.content()).await else {
  824. continue
  825. };
  826. // Potentially decrypt the privmsg
  827. self.server.try_decrypt(&mut privmsg).await;
  828. // If the privmsg is intented for any of the given channels, add it as
  829. // a reply and mark it as seen in the seen_events tree.
  830. if !channels.contains(&privmsg.channel) {
  831. continue
  832. }
  833. let msg = format!("PRIVMSG {} :{}", privmsg.channel, privmsg.msg);
  834. replies.push(ReplyType::Client((privmsg.nick, msg)));
  835. if let Err(e) = self.mark_seen(event_id).await {
  836. error!("[IRC CLIENT] (get_history) self.mark_seen({}) failed: {}", event_id, e);
  837. return Err(e)
  838. }
  839. }
  840. Ok(replies)
  841. }
  842. }