command.rs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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) {
  172. return Ok(self.welcome().await)
  173. }
  174. return Ok(vec![])
  175. }
  176. _ => {}
  177. }
  178. self.penalty.fetch_add(1, SeqCst);
  179. Ok(vec![ReplyType::Server((
  180. ERR_NEEDMOREPARAMS,
  181. format!("{} CAP :{}", nick, INVALID_SYNTAX),
  182. ))])
  183. }
  184. /// `INFO [<target>]`
  185. ///
  186. /// Gives information about the `<target>` server, or the current server if
  187. /// `<target>` is not used. The information includes the server's version,
  188. /// when it was compiled, the patch level, when it was started, and any
  189. /// other information which might be relevant.
  190. pub async fn handle_cmd_info(&self, _args: &str) -> Result<Vec<ReplyType>> {
  191. if !self.registered.load(SeqCst) {
  192. self.penalty.fetch_add(1, SeqCst);
  193. return Ok(vec![ReplyType::Server((
  194. ERR_NOTREGISTERED,
  195. format!("* :{}", NOT_REGISTERED),
  196. ))])
  197. }
  198. let nick = self.nickname.read().await.clone();
  199. let replies = vec![
  200. ReplyType::Server((
  201. RPL_INFO,
  202. format!("{} :DarkIRC {}", nick, env!("CARGO_PKG_VERSION")),
  203. )),
  204. ReplyType::Server((RPL_ENDOFINFO, format!("{} :End of INFO list", nick))),
  205. ];
  206. Ok(replies)
  207. }
  208. /// `JOIN <channels> [<keys>]`
  209. ///
  210. /// Makes the client join the channels in the list `<channels>`.
  211. /// Passwords can be used in the list `<keys>`. If the channels do not
  212. /// exist, they will be created.
  213. pub async fn handle_cmd_join(&self, args: &str) -> Result<Vec<ReplyType>> {
  214. if !self.registered.load(SeqCst) {
  215. self.penalty.fetch_add(1, SeqCst);
  216. return Ok(vec![ReplyType::Server((
  217. ERR_NOTREGISTERED,
  218. format!("* :{}", NOT_REGISTERED),
  219. ))])
  220. }
  221. // Client's (already) active channels
  222. let mut active_channels = self.channels.write().await;
  223. // Here we'll hold valid channel names.
  224. let mut channels = HashSet::new();
  225. // Let's scan through our channels. For now we'll only support
  226. // channel names starting with a single '#' character.
  227. let nick = self.nickname.read().await.to_string();
  228. let tokens = args.split_ascii_whitespace();
  229. for channel in tokens {
  230. if !channel.starts_with('#') {
  231. self.penalty.fetch_add(1, SeqCst);
  232. return Ok(vec![ReplyType::Server((
  233. ERR_NEEDMOREPARAMS,
  234. format!("{} JOIN :{}", nick, INVALID_SYNTAX),
  235. ))])
  236. }
  237. if !active_channels.contains(channel) {
  238. channels.insert(channel.to_string());
  239. }
  240. }
  241. // We need at least one channel.
  242. if channels.is_empty() {
  243. self.penalty.fetch_add(1, SeqCst);
  244. return Ok(vec![ReplyType::Server((
  245. ERR_NEEDMOREPARAMS,
  246. format!("{} JOIN :{}", nick, INVALID_SYNTAX),
  247. ))])
  248. }
  249. // Weechat sends channels as `#chan1,#chan2,#chan3`. Handle it.
  250. if channels.len() == 1 {
  251. let list = channels.iter().next().unwrap().clone();
  252. channels.remove(list.as_str());
  253. for channel in list.split(',') {
  254. if !channel.starts_with('#') || channel.as_bytes().len() > MAX_NICK_LEN {
  255. self.penalty.fetch_add(1, SeqCst);
  256. return Ok(vec![ReplyType::Server((
  257. ERR_NEEDMOREPARAMS,
  258. format!("{} JOIN :{}", nick, INVALID_SYNTAX),
  259. ))])
  260. }
  261. channels.insert(channel.to_string());
  262. }
  263. }
  264. // Create new channels for this client and construct replies.
  265. let mut server_channels = self.server.channels.write().await;
  266. let mut replies = vec![];
  267. for channel in channels.iter() {
  268. // Insert the channel name into the set of client's active channels
  269. active_channels.insert(channel.clone());
  270. // Create or update the channel on the server side.
  271. if let Some(server_chan) = server_channels.get_mut(channel) {
  272. server_chan.nicks.insert(nick.clone());
  273. } else {
  274. let chan = IrcChannel {
  275. topic: String::new(),
  276. nicks: HashSet::from([nick.clone()]),
  277. saltbox: None,
  278. };
  279. server_channels.insert(channel.clone(), chan);
  280. }
  281. // Create the replies
  282. replies.push(ReplyType::Client((nick.clone(), format!("JOIN :{}", channel))));
  283. replies.push(ReplyType::Server((
  284. RPL_NAMREPLY,
  285. format!("{} = {} :{}", nick, channel, nick),
  286. )));
  287. replies.push(ReplyType::Server((
  288. RPL_ENDOFNAMES,
  289. format!("{} {} :End of NAMES list", nick, channel),
  290. )));
  291. if let Some(chan) = server_channels.get(channel) {
  292. if !chan.topic.is_empty() {
  293. replies.push(ReplyType::Client((
  294. nick.clone(),
  295. format!("TOPIC {} :{}", channel, chan.topic),
  296. )));
  297. }
  298. }
  299. }
  300. // Drop the locks as they're used in get_history()
  301. drop(active_channels);
  302. drop(server_channels);
  303. // Potentially extend the replies with channel history
  304. replies.append(&mut self.get_history(&channels).await.unwrap());
  305. Ok(replies)
  306. }
  307. /// `LIST [<channels> [<server>]]`
  308. ///
  309. /// List all channels on the server. If the list `<channels>` is given, it
  310. /// will return the channel topics. If `<server>` is given, the command will
  311. /// be sent to `<server>` for evaluation.
  312. pub async fn handle_cmd_list(&self, _args: &str) -> Result<Vec<ReplyType>> {
  313. if !self.registered.load(SeqCst) {
  314. self.penalty.fetch_add(1, SeqCst);
  315. return Ok(vec![ReplyType::Server((
  316. ERR_NOTREGISTERED,
  317. format!("* :{}", NOT_REGISTERED),
  318. ))])
  319. }
  320. let nick = self.nickname.read().await.to_string();
  321. let mut list = vec![];
  322. for (name, channel) in self.server.channels.read().await.iter() {
  323. list.push(format!("{} {} {} :{}", nick, name, channel.nicks.len(), channel.topic));
  324. }
  325. let mut replies = vec![];
  326. replies.push(ReplyType::Server((RPL_LISTSTART, format!("{} Channel :Users Name", nick))));
  327. for chan in list {
  328. replies.push(ReplyType::Server((RPL_LIST, chan)));
  329. }
  330. replies.push(ReplyType::Server((RPL_LISTEND, format!("{} :End of /LIST", nick))));
  331. Ok(replies)
  332. }
  333. /// `MODE <nickname> <flags>`
  334. /// `MODE <channel> <flags>`
  335. ///
  336. /// The MODE command has two uses. It can be used to set both user and
  337. /// channel modes.
  338. pub async fn handle_cmd_mode(&self, args: &str) -> Result<Vec<ReplyType>> {
  339. if !self.registered.load(SeqCst) {
  340. self.penalty.fetch_add(1, SeqCst);
  341. return Ok(vec![ReplyType::Server((
  342. ERR_NOTREGISTERED,
  343. format!("* :{}", NOT_REGISTERED),
  344. ))])
  345. }
  346. let nick = self.nickname.read().await.to_string();
  347. let mut tokens = args.split_ascii_whitespace();
  348. let Some(target) = tokens.next() else {
  349. self.penalty.fetch_add(1, SeqCst);
  350. return Ok(vec![ReplyType::Server((
  351. ERR_NEEDMOREPARAMS,
  352. format!("{} MODE :{}", nick, INVALID_SYNTAX),
  353. ))])
  354. };
  355. if target == nick {
  356. return Ok(vec![ReplyType::Server((RPL_UMODEIS, format!("{} +", nick)))])
  357. }
  358. if !target.starts_with('#') {
  359. return Ok(vec![ReplyType::Server((
  360. ERR_USERSDONTMATCH,
  361. format!("{} :Can't set/get mode for other users", nick),
  362. ))])
  363. }
  364. if !self.server.channels.read().await.contains_key(target) {
  365. return Ok(vec![ReplyType::Server((
  366. ERR_NOSUCHNICK,
  367. format!("{} {} :No such nick or channel name", nick, target),
  368. ))])
  369. }
  370. Ok(vec![ReplyType::Server((RPL_CHANNELMODEIS, format!("{} {} +", nick, target)))])
  371. }
  372. /// `MOTD [<server>]`
  373. ///
  374. /// Returns the message of the day on `<server>` or the current server if
  375. /// it is not stated.
  376. pub async fn handle_cmd_motd(&self, _args: &str) -> Result<Vec<ReplyType>> {
  377. let nick = self.nickname.read().await.to_string();
  378. Ok(vec![
  379. ReplyType::Server((
  380. RPL_MOTDSTART,
  381. format!("{} :- {} message of the day", nick, SERVER_NAME),
  382. )),
  383. ReplyType::Server((RPL_MOTD, format!("{} :Let there be dark!", nick))),
  384. ReplyType::Server((RPL_ENDOFMOTD, format!("{} :End of /MOTD command.", nick))),
  385. ])
  386. }
  387. /// `NAMES [<channel>]`
  388. ///
  389. /// Returns a list of who is on the list of `<channel>`, by channel name.
  390. /// If `<channel>` is not used, all users are shown. They are grouped by
  391. /// channel name with all users who are not on a channel being shown as
  392. /// part of channel "*".
  393. pub async fn handle_cmd_names(&self, args: &str) -> Result<Vec<ReplyType>> {
  394. if !self.registered.load(SeqCst) {
  395. self.penalty.fetch_add(1, SeqCst);
  396. return Ok(vec![ReplyType::Server((
  397. ERR_NOTREGISTERED,
  398. format!("* :{}", NOT_REGISTERED),
  399. ))])
  400. }
  401. let nick = self.nickname.read().await.to_string();
  402. let mut tokens = args.split_ascii_whitespace();
  403. let mut replies = vec![];
  404. // If a channel was requested, reply only with that one.
  405. // Otherwise, return info for all known channels.
  406. if let Some(req_chan) = tokens.next() {
  407. if let Some(chan) = self.server.channels.read().await.get(req_chan) {
  408. let nicks: Vec<String> = chan.nicks.iter().cloned().collect();
  409. replies.push(ReplyType::Server((
  410. RPL_NAMREPLY,
  411. format!("{} = {} :{}", nick, req_chan, nicks.join(" ")),
  412. )));
  413. }
  414. replies.push(ReplyType::Server((
  415. RPL_ENDOFNAMES,
  416. format!("{} {} :End of NAMES list", nick, req_chan),
  417. )));
  418. Ok(replies)
  419. } else {
  420. for (name, chan) in self.server.channels.read().await.iter() {
  421. let nicks: Vec<String> = chan.nicks.iter().cloned().collect();
  422. replies.push(ReplyType::Server((
  423. RPL_NAMREPLY,
  424. format!("{} = {} :{}", nick, name, nicks.join(" ")),
  425. )));
  426. }
  427. replies.push(ReplyType::Server((
  428. RPL_ENDOFNAMES,
  429. format!("{} * :End of NAMES list", nick),
  430. )));
  431. Ok(replies)
  432. }
  433. }
  434. /// `NICK <nickname>`
  435. ///
  436. /// Allows a client to change their IRC nickname.
  437. pub async fn handle_cmd_nick(&self, args: &str) -> Result<Vec<ReplyType>> {
  438. // Parse the line
  439. let mut tokens = args.split_ascii_whitespace();
  440. // Reference the current nickname
  441. let old_nick = self.nickname.read().await.to_string();
  442. let Some(nickname) = tokens.next() else {
  443. self.penalty.fetch_add(1, SeqCst);
  444. return Ok(vec![ReplyType::Server((
  445. ERR_NEEDMOREPARAMS,
  446. format!("{} NICK :{}", old_nick, INVALID_SYNTAX),
  447. ))])
  448. };
  449. // Forbid disallowed characters.
  450. // The next() call is done to check for ASCII whitespace in the nick.
  451. if tokens.next().is_some() || nickname.starts_with(':') || nickname.starts_with('#') {
  452. self.penalty.fetch_add(1, SeqCst);
  453. return Ok(vec![ReplyType::Server((
  454. ERR_ERRONEOUSNICKNAME,
  455. format!("{} {} :Erroneous nickname", old_nick, nickname),
  456. ))])
  457. }
  458. // Disallow too long nicks
  459. if nickname.as_bytes().len() > MAX_NICK_LEN {
  460. self.penalty.fetch_add(1, SeqCst);
  461. return Ok(vec![ReplyType::Server((
  462. ERR_ERRONEOUSNICKNAME,
  463. format!("{} {} :Nickname too long", old_nick, nickname),
  464. ))])
  465. }
  466. // Set the new nickname
  467. *self.nickname.write().await = nickname.to_string();
  468. // If the username is set, we can complete the registration
  469. if *self.username.read().await != "*" && !self.registered.load(SeqCst) {
  470. self.registered.store(true, SeqCst);
  471. if self.reg_paused.load(SeqCst) {
  472. return Ok(vec![])
  473. } else {
  474. return Ok(self.welcome().await)
  475. }
  476. }
  477. // If we were registered, we send a client reply about it.
  478. if self.registered.load(SeqCst) {
  479. Ok(vec![ReplyType::Client((old_nick, format!("NICK :{}", nickname)))])
  480. } else {
  481. // Otherwise, we don't reply.
  482. Ok(vec![])
  483. }
  484. }
  485. /// `PART <channel>`
  486. ///
  487. /// Causes a user to leave the channel `<channel>`.
  488. pub async fn handle_cmd_part(&self, args: &str) -> Result<Vec<ReplyType>> {
  489. if !self.registered.load(SeqCst) {
  490. self.penalty.fetch_add(1, SeqCst);
  491. return Ok(vec![ReplyType::Server((
  492. ERR_NOTREGISTERED,
  493. format!("* :{}", NOT_REGISTERED),
  494. ))])
  495. }
  496. let nick = self.nickname.read().await.to_string();
  497. let mut tokens = args.split_ascii_whitespace();
  498. let Some(channel) = tokens.next() else {
  499. self.penalty.fetch_add(1, SeqCst);
  500. return Ok(vec![ReplyType::Server((
  501. ERR_NEEDMOREPARAMS,
  502. format!("{} PART :{}", nick, INVALID_SYNTAX),
  503. ))])
  504. };
  505. if !channel.starts_with('#') {
  506. self.penalty.fetch_add(1, SeqCst);
  507. return Ok(vec![ReplyType::Server((
  508. ERR_NEEDMOREPARAMS,
  509. format!("{} PART :{}", nick, INVALID_SYNTAX),
  510. ))])
  511. }
  512. let mut active_channels = self.channels.write().await;
  513. if !active_channels.contains(channel) {
  514. return Ok(vec![ReplyType::Server((
  515. ERR_NOSUCHCHANNEL,
  516. format!("{} {} :No such channel", nick, channel),
  517. ))])
  518. }
  519. // Remove the channel from the client's channel list
  520. active_channels.remove(channel);
  521. let replies = vec![ReplyType::Client((nick, format!("PART {} :Bye", channel)))];
  522. Ok(replies)
  523. }
  524. /// `PING <server1>`
  525. ///
  526. /// Tests a connection. A PING message results in a PONG reply.
  527. pub async fn handle_cmd_ping(&self, args: &str) -> Result<Vec<ReplyType>> {
  528. if !self.registered.load(SeqCst) {
  529. self.penalty.fetch_add(1, SeqCst);
  530. return Ok(vec![ReplyType::Server((
  531. ERR_NOTREGISTERED,
  532. format!("* :{}", NOT_REGISTERED),
  533. ))])
  534. }
  535. let mut tokens = args.split_ascii_whitespace();
  536. let Some(origin) = tokens.next() else {
  537. self.penalty.fetch_add(1, SeqCst);
  538. return Ok(vec![ReplyType::Server((
  539. ERR_NOORIGIN,
  540. format!("{} :No origin specified", self.nickname.read().await),
  541. ))])
  542. };
  543. Ok(vec![ReplyType::Pong(origin.to_string())])
  544. }
  545. /// `PRIVMSG <msgtarget> <message>`
  546. ///
  547. /// Sends `<message>` to `<msgtarget>`. The target is usually a user or
  548. /// a channel.
  549. pub async fn handle_cmd_privmsg(&self, args: &str) -> Result<Vec<ReplyType>> {
  550. if !self.registered.load(SeqCst) {
  551. self.penalty.fetch_add(1, SeqCst);
  552. return Ok(vec![ReplyType::Server((
  553. ERR_NOTREGISTERED,
  554. format!("* :{}", NOT_REGISTERED),
  555. ))])
  556. }
  557. let nick = self.nickname.read().await.to_string();
  558. let mut tokens = args.split_ascii_whitespace();
  559. let Some(target) = tokens.next() else {
  560. return Ok(vec![ReplyType::Server((
  561. ERR_NORECIPIENT,
  562. format!("{} :No recipient given (PRIVMSG)", nick),
  563. ))])
  564. };
  565. let Some(message) = tokens.next() else {
  566. return Ok(vec![ReplyType::Server((
  567. ERR_NOTEXTTOSEND,
  568. format!("{} :No text to send", nick),
  569. ))])
  570. };
  571. if !message.starts_with(':') {
  572. return Ok(vec![ReplyType::Server((
  573. ERR_NOTEXTTOSEND,
  574. format!("{} :No text to send", nick),
  575. ))])
  576. }
  577. // We only send a client reply if the message is for ourself.
  578. // Anything else is rendered by the IRC client and not supposed
  579. // to be echoed by the IRC serer.
  580. if target == nick {
  581. return Ok(vec![ReplyType::Client((
  582. target.to_string(),
  583. format!("PRIVMSG {} {}", target, message),
  584. ))])
  585. }
  586. // If it's a DM and we don't have an encryption key, we will
  587. // refuse to send it. Send ERR_NORECIPIENT to the client.
  588. if !target.starts_with('#') && !self.server.contacts.read().await.contains_key(target) {
  589. return Ok(vec![ReplyType::Server((ERR_NOSUCHNICK, format!("{} :{}", nick, target)))])
  590. }
  591. Ok(vec![])
  592. }
  593. /// `REHASH`
  594. ///
  595. /// Causes the server to re-read and re-process its configuration file(s).
  596. pub async fn handle_cmd_rehash(&self, _args: &str) -> Result<Vec<ReplyType>> {
  597. info!("Attempting to rehash server...");
  598. if let Err(e) = self.server.rehash().await {
  599. error!("Failed to rehash server: {}", e);
  600. }
  601. Ok(vec![])
  602. }
  603. /// `TOPIC <channel> [<topic>]`
  604. ///
  605. /// Used to get the channel topic on `<channel>`. If `<topic>` is given, it
  606. /// sets the channel topic to `<topic>`.
  607. pub async fn handle_cmd_topic(&self, args: &str) -> Result<Vec<ReplyType>> {
  608. if !self.registered.load(SeqCst) {
  609. self.penalty.fetch_add(1, SeqCst);
  610. return Ok(vec![ReplyType::Server((
  611. ERR_NOTREGISTERED,
  612. format!("* :{}", NOT_REGISTERED),
  613. ))])
  614. }
  615. let nick = self.nickname.read().await.to_string();
  616. let mut tokens = args.split_ascii_whitespace();
  617. let Some(channel) = tokens.next() else {
  618. self.penalty.fetch_add(1, SeqCst);
  619. return Ok(vec![ReplyType::Server((
  620. ERR_NEEDMOREPARAMS,
  621. format!("{} TOPIC :{}", nick, INVALID_SYNTAX),
  622. ))])
  623. };
  624. if !self.server.channels.read().await.contains_key(channel) {
  625. return Ok(vec![ReplyType::Server((
  626. ERR_NOSUCHCHANNEL,
  627. format!("{} {} :No such channel", nick, channel),
  628. ))])
  629. }
  630. // If there's a topic, we'll set it, otherwise return the set topic.
  631. let Some(topic) = tokens.next() else {
  632. let topic = self.server.channels.read().await.get(channel).unwrap().topic.clone();
  633. if topic.is_empty() {
  634. return Ok(vec![ReplyType::Server((
  635. RPL_NOTOPIC,
  636. format!("{} {} :No topic is set", nick, channel),
  637. ))])
  638. } else {
  639. return Ok(vec![ReplyType::Server((
  640. RPL_TOPIC,
  641. format!("{} {} :{}", nick, channel, topic),
  642. ))])
  643. }
  644. };
  645. // Set the new topic
  646. self.server.channels.write().await.get_mut(channel).unwrap().topic =
  647. topic.strip_prefix(':').unwrap().to_string();
  648. // Send reply
  649. let replies = vec![ReplyType::Client((nick, format!("TOPIC {} {}", channel, topic)))];
  650. Ok(replies)
  651. }
  652. /// `USER <user> <mode> <unused> <realname>`
  653. ///
  654. /// This command is used at the beginning of a connection to specify the
  655. /// username, hostname, real name, and the initial user modes of the
  656. /// connecting client. `<realname>` may contain spaces, and thus must be
  657. /// prefixed with a colon.
  658. pub async fn handle_cmd_user(&self, args: &str) -> Result<Vec<ReplyType>> {
  659. if self.registered.load(SeqCst) {
  660. self.penalty.fetch_add(1, SeqCst);
  661. return Ok(vec![ReplyType::Server((
  662. ERR_ALREADYREGISTERED,
  663. format!("{} :{}", self.nickname.read().await, ALREADY_REGISTERED),
  664. ))])
  665. }
  666. // Parse the line
  667. let nick = self.nickname.read().await.to_string();
  668. let mut tokens = args.split_ascii_whitespace();
  669. let Some(username) = tokens.next() else {
  670. self.penalty.fetch_add(1, SeqCst);
  671. return Ok(vec![ReplyType::Server((
  672. ERR_NEEDMOREPARAMS,
  673. format!("{} USER :{}", nick, INVALID_SYNTAX),
  674. ))])
  675. };
  676. // Mode syntax is currently ignored, but should be part of the command
  677. let Some(_mode) = tokens.next() else {
  678. self.penalty.fetch_add(1, SeqCst);
  679. return Ok(vec![ReplyType::Server((
  680. ERR_NEEDMOREPARAMS,
  681. format!("{} USER :{}", nick, INVALID_SYNTAX),
  682. ))])
  683. };
  684. // Next token is unused per RFC, but should be part of the command
  685. let Some(_unused) = tokens.next() else {
  686. self.penalty.fetch_add(1, SeqCst);
  687. return Ok(vec![ReplyType::Server((
  688. ERR_NEEDMOREPARAMS,
  689. format!("{} USER :{}", nick, INVALID_SYNTAX),
  690. ))])
  691. };
  692. // The final token should be realname and should start with a colon
  693. let Some(realname) = tokens.next() else {
  694. self.penalty.fetch_add(1, SeqCst);
  695. return Ok(vec![ReplyType::Server((
  696. ERR_NEEDMOREPARAMS,
  697. format!("{} USER :{}", nick, INVALID_SYNTAX),
  698. ))])
  699. };
  700. if !realname.starts_with(':') {
  701. self.penalty.fetch_add(1, SeqCst);
  702. return Ok(vec![ReplyType::Server((
  703. ERR_NEEDMOREPARAMS,
  704. format!("{} USER :{}", nick, INVALID_SYNTAX),
  705. ))])
  706. }
  707. *self.username.write().await = username.to_string();
  708. *self.realname.write().await = realname.to_string();
  709. // If the nickname is set, we can complete the registration
  710. if nick != "*" {
  711. self.registered.store(true, SeqCst);
  712. if self.reg_paused.load(SeqCst) {
  713. return Ok(vec![])
  714. } else {
  715. return Ok(self.welcome().await)
  716. }
  717. }
  718. // Otherwise, we don't have to reply.
  719. Ok(vec![])
  720. }
  721. /// `VERSION`
  722. ///
  723. /// Returns the version of the server.
  724. pub async fn handle_cmd_version(&self, _args: &str) -> Result<Vec<ReplyType>> {
  725. if !self.registered.load(SeqCst) {
  726. self.penalty.fetch_add(1, SeqCst);
  727. return Ok(vec![ReplyType::Server((
  728. ERR_NOTREGISTERED,
  729. format!("* :{}", NOT_REGISTERED),
  730. ))])
  731. }
  732. let replies = vec![ReplyType::Server((
  733. RPL_VERSION,
  734. format!(
  735. "{} {} {} :Let there be dark!",
  736. self.nickname.read().await,
  737. env!("CARGO_PKG_VERSION"),
  738. SERVER_NAME
  739. ),
  740. ))];
  741. Ok(replies)
  742. }
  743. /// Internal function that constructs the welcome message.
  744. async fn welcome(&self) -> Vec<ReplyType> {
  745. let nick = self.nickname.read().await.to_string();
  746. let mut replies = vec![
  747. ReplyType::Server((RPL_WELCOME, format!("{} :{}", nick, WELCOME))),
  748. ReplyType::Server((
  749. RPL_YOURHOST,
  750. format!(
  751. "{} :Your host is irc.dark.fi, running version {}",
  752. nick,
  753. env!("CARGO_PKG_VERSION")
  754. ),
  755. )),
  756. ];
  757. // Append the MOTD
  758. replies.append(&mut self.handle_cmd_motd("").await.unwrap());
  759. // If we have any configured autojoin channels, let's join the user
  760. // and set their topics, if any.
  761. let mut config_chans = self.server.channels.write().await;
  762. let mut autojoin_chans = HashSet::new();
  763. for channel in self.server.autojoin.read().await.iter() {
  764. autojoin_chans.insert(channel.clone());
  765. }
  766. for channel in autojoin_chans.iter() {
  767. replies.push(ReplyType::Client((nick.clone(), format!("JOIN :{}", channel))));
  768. replies.push(ReplyType::Server((
  769. RPL_NAMREPLY,
  770. format!("{} = {} :{}", nick, channel, nick),
  771. )));
  772. replies.push(ReplyType::Server((
  773. RPL_ENDOFNAMES,
  774. format!("{} {} :End of NAMES list", nick, channel),
  775. )));
  776. if let Some(chan) = config_chans.get_mut(channel) {
  777. if !chan.topic.is_empty() {
  778. replies.push(ReplyType::Client((
  779. nick.clone(),
  780. format!("TOPIC {} :{}", channel, chan.topic),
  781. )));
  782. }
  783. // Insert the client into the channel nicklist
  784. chan.nicks.insert(nick.clone());
  785. }
  786. }
  787. // Drop the write lock, it's used in get_history()
  788. drop(config_chans);
  789. // Potentially extend replies with history
  790. autojoin_chans.insert(self.nickname.read().await.to_string());
  791. replies.append(&mut self.get_history(&autojoin_chans).await.unwrap());
  792. replies
  793. }
  794. /// Internal function that scans the DAG and returns events for
  795. /// given channels. Will return empty if no_history CAP is requested.
  796. async fn get_history(&self, channels: &HashSet<String>) -> Result<Vec<ReplyType>> {
  797. if channels.is_empty() || *self.caps.read().await.get("no-history").unwrap() {
  798. return Ok(vec![])
  799. }
  800. // Fetch and order all the events from the DAG
  801. let dag_events = self.server.darkirc.event_graph.order_events().await;
  802. // Here we'll hold the events in order we'll push to the client
  803. let mut replies = vec![];
  804. for event_id in dag_events.iter() {
  805. // If it was seen, skip
  806. match self.is_seen(event_id).await {
  807. Ok(true) => continue,
  808. Ok(false) => {}
  809. Err(e) => {
  810. error!("[IRC CLIENT] (get_history) self.is_seen({}) failed: {}", event_id, e);
  811. return Err(e)
  812. }
  813. }
  814. // Get the event from the DAG
  815. let event = self.server.darkirc.event_graph.dag_get(event_id).await.unwrap().unwrap();
  816. // Try to deserialize it. (Here we skip errors)
  817. let Ok((mut privmsg, _)) = deserialize_async_partial(event.content()).await else {
  818. continue
  819. };
  820. // Potentially decrypt the privmsg
  821. self.server.try_decrypt(&mut privmsg).await;
  822. // If the privmsg is intented for any of the given channels, add it as
  823. // a reply and mark it as seen in the seen_events tree.
  824. if !channels.contains(&privmsg.channel) {
  825. continue
  826. }
  827. let msg = format!("PRIVMSG {} :{}", privmsg.channel, privmsg.msg);
  828. replies.push(ReplyType::Client((privmsg.nick, msg)));
  829. if let Err(e) = self.mark_seen(event_id).await {
  830. error!("[IRC CLIENT] (get_history) self.mark_seen({}) failed: {}", event_id, e);
  831. return Err(e)
  832. }
  833. }
  834. Ok(replies)
  835. }
  836. }