1#[cfg(feature = "ctcp")]
51use chrono::prelude::*;
52use futures_util::{
53 future::{FusedFuture, Future},
54 ready,
55 stream::{FusedStream, Stream},
56};
57use futures_util::{
58 sink::Sink as _,
59 stream::{SplitSink, SplitStream, StreamExt as _},
60};
61use parking_lot::RwLock;
62use std::{
63 collections::HashMap,
64 fmt,
65 path::Path,
66 pin::Pin,
67 sync::Arc,
68 task::{Context, Poll},
69};
70use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
71
72use crate::{
73 client::{
74 conn::Connection,
75 data::{Config, User},
76 },
77 error,
78 proto::{
79 mode::ModeType,
80 CapSubCommand::{END, LS, REQ},
81 Capability, ChannelMode, Command,
82 Command::{
83 ChannelMODE, AUTHENTICATE, CAP, INVITE, JOIN, KICK, KILL, NICK, NICKSERV, NOTICE, OPER,
84 PART, PASS, PONG, PRIVMSG, QUIT, SAMODE, SANICK, TOPIC, USER,
85 },
86 Message, Mode, NegotiationVersion, Response,
87 },
88};
89
90pub mod conn;
91pub mod data;
92mod mock;
93pub mod prelude;
94pub mod transport;
95
96macro_rules! pub_state_base {
97 () => {
98 pub fn send_mode<S, T>(&self, target: S, modes: &[Mode<T>]) -> error::Result<()>
100 where
101 S: fmt::Display,
102 T: ModeType,
103 {
104 self.send(T::mode(&target.to_string(), modes))
105 }
106
107 pub fn send_join<S>(&self, chanlist: S) -> error::Result<()>
109 where
110 S: fmt::Display,
111 {
112 self.send(JOIN(chanlist.to_string(), None, None))
113 }
114
115 pub fn send_join_with_keys<S1, S2>(&self, chanlist: S1, keylist: S2) -> error::Result<()>
117 where
118 S1: fmt::Display,
119 S2: fmt::Display,
120 {
121 self.send(JOIN(chanlist.to_string(), Some(keylist.to_string()), None))
122 }
123
124 pub fn send_notice<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
126 where
127 S1: fmt::Display,
128 S2: fmt::Display,
129 {
130 let message = message.to_string();
131 for line in message.split("\r\n") {
132 self.send(NOTICE(target.to_string(), line.to_string()))?
133 }
134 Ok(())
135 }
136 };
137}
138
139macro_rules! pub_sender_base {
140 () => {
141 pub fn send_cap_ls(&self, version: NegotiationVersion) -> error::Result<()> {
143 self.send(Command::CAP(
144 None,
145 LS,
146 match version {
147 NegotiationVersion::V301 => None,
148 NegotiationVersion::V302 => Some("302".to_owned()),
149 },
150 None,
151 ))
152 }
153
154 pub fn send_cap_req(&self, extensions: &[Capability]) -> error::Result<()> {
156 let append = |mut s: String, c| {
157 s.push_str(c);
158 s.push(' ');
159 s
160 };
161 let mut exts = extensions
162 .iter()
163 .map(|c| c.as_ref())
164 .fold(String::new(), append);
165 let len = exts.len() - 1;
166 exts.truncate(len);
167 self.send(CAP(None, REQ, None, Some(exts)))
168 }
169
170 pub fn send_sasl<S: fmt::Display>(&self, data: S) -> error::Result<()> {
172 self.send(AUTHENTICATE(data.to_string()))
173 }
174
175 pub fn send_sasl_plain(&self) -> error::Result<()> {
177 self.send_sasl("PLAIN")
178 }
179
180 pub fn send_sasl_external(&self) -> error::Result<()> {
182 self.send_sasl("EXTERNAL")
183 }
184
185 pub fn send_sasl_abort(&self) -> error::Result<()> {
187 self.send_sasl("*")
188 }
189
190 pub fn send_pong<S>(&self, msg: S) -> error::Result<()>
192 where
193 S: fmt::Display,
194 {
195 self.send(PONG(msg.to_string(), None))
196 }
197
198 pub fn send_part<S>(&self, chanlist: S) -> error::Result<()>
200 where
201 S: fmt::Display,
202 {
203 self.send(PART(chanlist.to_string(), None))
204 }
205
206 pub fn send_oper<S1, S2>(&self, username: S1, password: S2) -> error::Result<()>
208 where
209 S1: fmt::Display,
210 S2: fmt::Display,
211 {
212 self.send(OPER(username.to_string(), password.to_string()))
213 }
214
215 pub fn send_privmsg<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
220 where
221 S1: fmt::Display,
222 S2: fmt::Display,
223 {
224 let message = message.to_string();
225 for line in message.split("\r\n") {
226 self.send(PRIVMSG(target.to_string(), line.to_string()))?
227 }
228 Ok(())
229 }
230
231 pub fn send_topic<S1, S2>(&self, channel: S1, topic: S2) -> error::Result<()>
234 where
235 S1: fmt::Display,
236 S2: fmt::Display,
237 {
238 let topic = topic.to_string();
239 self.send(TOPIC(
240 channel.to_string(),
241 if topic.is_empty() { None } else { Some(topic) },
242 ))
243 }
244
245 pub fn send_kill<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
247 where
248 S1: fmt::Display,
249 S2: fmt::Display,
250 {
251 self.send(KILL(target.to_string(), message.to_string()))
252 }
253
254 pub fn send_kick<S1, S2, S3>(
257 &self,
258 chanlist: S1,
259 nicklist: S2,
260 message: S3,
261 ) -> error::Result<()>
262 where
263 S1: fmt::Display,
264 S2: fmt::Display,
265 S3: fmt::Display,
266 {
267 let message = message.to_string();
268 self.send(KICK(
269 chanlist.to_string(),
270 nicklist.to_string(),
271 if message.is_empty() {
272 None
273 } else {
274 Some(message)
275 },
276 ))
277 }
278
279 pub fn send_samode<S1, S2, S3>(
282 &self,
283 target: S1,
284 mode: S2,
285 modeparams: S3,
286 ) -> error::Result<()>
287 where
288 S1: fmt::Display,
289 S2: fmt::Display,
290 S3: fmt::Display,
291 {
292 let modeparams = modeparams.to_string();
293 self.send(SAMODE(
294 target.to_string(),
295 mode.to_string(),
296 if modeparams.is_empty() {
297 None
298 } else {
299 Some(modeparams)
300 },
301 ))
302 }
303
304 pub fn send_sanick<S1, S2>(&self, old_nick: S1, new_nick: S2) -> error::Result<()>
306 where
307 S1: fmt::Display,
308 S2: fmt::Display,
309 {
310 self.send(SANICK(old_nick.to_string(), new_nick.to_string()))
311 }
312
313 pub fn send_invite<S1, S2>(&self, nick: S1, chan: S2) -> error::Result<()>
315 where
316 S1: fmt::Display,
317 S2: fmt::Display,
318 {
319 self.send(INVITE(nick.to_string(), chan.to_string()))
320 }
321
322 pub fn send_quit<S>(&self, msg: S) -> error::Result<()>
325 where
326 S: fmt::Display,
327 {
328 let msg = msg.to_string();
329 self.send(QUIT(Some(if msg.is_empty() {
330 "Powered by Rust.".to_string()
331 } else {
332 msg
333 })))
334 }
335
336 #[cfg(feature = "ctcp")]
339 pub fn send_ctcp<S1, S2>(&self, target: S1, msg: S2) -> error::Result<()>
340 where
341 S1: fmt::Display,
342 S2: fmt::Display,
343 {
344 let msg = msg.to_string();
345 for line in msg.split("\r\n") {
346 self.send(PRIVMSG(
347 target.to_string(),
348 format!("\u{001}{}\u{001}", line),
349 ))?
350 }
351 Ok(())
352 }
353
354 #[cfg(feature = "ctcp")]
357 pub fn send_action<S1, S2>(&self, target: S1, msg: S2) -> error::Result<()>
358 where
359 S1: fmt::Display,
360 S2: fmt::Display,
361 {
362 self.send_ctcp(target, &format!("ACTION {}", msg.to_string())[..])
363 }
364
365 #[cfg(feature = "ctcp")]
368 pub fn send_finger<S: fmt::Display>(&self, target: S) -> error::Result<()>
369 where
370 S: fmt::Display,
371 {
372 self.send_ctcp(target, "FINGER")
373 }
374
375 #[cfg(feature = "ctcp")]
378 pub fn send_version<S>(&self, target: S) -> error::Result<()>
379 where
380 S: fmt::Display,
381 {
382 self.send_ctcp(target, "VERSION")
383 }
384
385 #[cfg(feature = "ctcp")]
388 pub fn send_source<S>(&self, target: S) -> error::Result<()>
389 where
390 S: fmt::Display,
391 {
392 self.send_ctcp(target, "SOURCE")
393 }
394
395 #[cfg(feature = "ctcp")]
398 pub fn send_user_info<S>(&self, target: S) -> error::Result<()>
399 where
400 S: fmt::Display,
401 {
402 self.send_ctcp(target, "USERINFO")
403 }
404
405 #[cfg(feature = "ctcp")]
408 pub fn send_ctcp_ping<S>(&self, target: S) -> error::Result<()>
409 where
410 S: fmt::Display,
411 {
412 let time = Local::now();
413 self.send_ctcp(target, &format!("PING {}", time.timestamp())[..])
414 }
415
416 #[cfg(feature = "ctcp")]
419 pub fn send_time<S>(&self, target: S) -> error::Result<()>
420 where
421 S: fmt::Display,
422 {
423 self.send_ctcp(target, "TIME")
424 }
425 };
426}
427
428#[derive(Debug)]
435pub struct ClientStream {
436 state: Arc<ClientState>,
437 stream: SplitStream<Connection>,
438 outgoing: Option<Outgoing>,
440}
441
442impl ClientStream {
443 pub async fn collect(mut self) -> error::Result<Vec<Message>> {
445 let mut output = Vec::new();
446
447 while let Some(message) = self.next().await {
448 match message {
449 Ok(message) => output.push(message),
450 Err(e) => return Err(e),
451 }
452 }
453
454 Ok(output)
455 }
456}
457
458impl FusedStream for ClientStream {
459 fn is_terminated(&self) -> bool {
460 false
461 }
462}
463
464impl Stream for ClientStream {
465 type Item = Result<Message, error::Error>;
466
467 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
468 if let Some(outgoing) = self.as_mut().outgoing.as_mut() {
469 match Pin::new(outgoing).poll(cx) {
470 Poll::Ready(Ok(())) => {
471 cx.waker().wake_by_ref();
473 return Poll::Ready(None);
474 }
475 Poll::Ready(Err(e)) => {
476 cx.waker().wake_by_ref();
477 return Poll::Ready(Some(Err(e)));
478 }
479 Poll::Pending => (),
480 }
481 }
482
483 match ready!(Pin::new(&mut self.as_mut().stream).poll_next(cx)) {
484 Some(Ok(msg)) => {
485 self.state.handle_message(&msg)?;
486 Poll::Ready(Some(Ok(msg)))
487 }
488 other => Poll::Ready(other),
489 }
490 }
491}
492
493#[derive(Debug)]
495struct ClientState {
496 sender: Sender,
497 config: Config,
499 chanlists: RwLock<HashMap<String, Vec<User>>>,
501 alt_nick_index: RwLock<usize>,
503 default_ghost_sequence: Vec<String>,
505}
506
507impl ClientState {
508 fn new(sender: Sender, config: Config) -> ClientState {
509 ClientState {
510 sender,
511 config,
512 chanlists: RwLock::new(HashMap::new()),
513 alt_nick_index: RwLock::new(0),
514 default_ghost_sequence: vec![String::from("GHOST")],
515 }
516 }
517
518 fn config(&self) -> &Config {
519 &self.config
520 }
521
522 fn send<M: Into<Message>>(&self, msg: M) -> error::Result<()> {
523 let msg = msg.into();
524 self.handle_sent_message(&msg)?;
525 self.sender.send(msg)
526 }
527
528 fn current_nickname(&self) -> &str {
530 let alt_nicks = self.config().alternate_nicknames();
531 let index = self.alt_nick_index.read();
532
533 match *index {
534 0 => self
535 .config()
536 .nickname()
537 .expect("current_nickname should not be callable if nickname is not defined."),
538 i => alt_nicks[i - 1].as_str(),
539 }
540 }
541
542 fn handle_sent_message(&self, msg: &Message) -> error::Result<()> {
544 log::trace!("[SENT] {}", msg.to_string());
545
546 if let PART(ref chan, _) = msg.command {
547 let _ = self.chanlists.write().remove(chan);
548 }
549
550 Ok(())
551 }
552
553 fn handle_message(&self, msg: &Message) -> error::Result<()> {
555 log::trace!("[RECV] {}", msg.to_string());
556 match msg.command {
557 JOIN(ref chan, _, _) => self.handle_join(msg.source_nickname().unwrap_or(""), chan),
558 PART(ref chan, _) => self.handle_part(msg.source_nickname().unwrap_or(""), chan),
559 KICK(ref chan, ref user, _) => self.handle_part(user, chan),
560 QUIT(_) => self.handle_quit(msg.source_nickname().unwrap_or("")),
561 NICK(ref new_nick) => {
562 self.handle_nick_change(msg.source_nickname().unwrap_or(""), new_nick)
563 }
564 ChannelMODE(ref chan, ref modes) => self.handle_mode(chan, modes),
565 PRIVMSG(ref target, ref body) => {
566 if body.starts_with('\u{001}') {
567 let tokens: Vec<_> = {
568 let end = if body.ends_with('\u{001}') && body.len() > 1 {
569 body.len() - 1
570 } else {
571 body.len()
572 };
573 body[1..end].split(' ').collect()
574 };
575 if target.starts_with('#') {
576 self.handle_ctcp(target, &tokens)?
577 } else if let Some(user) = msg.source_nickname() {
578 self.handle_ctcp(user, &tokens)?
579 }
580 }
581 }
582 Command::Response(Response::RPL_NAMREPLY, ref args) => self.handle_namreply(args),
583 Command::Response(Response::RPL_ENDOFMOTD, _)
584 | Command::Response(Response::ERR_NOMOTD, _) => {
585 self.send_nick_password()?;
586 self.send_umodes()?;
587
588 let config_chans = self.config().channels();
589 for chan in config_chans {
590 match self.config().channel_key(chan) {
591 Some(key) => self.send_join_with_keys::<&str, &str>(chan, key)?,
592 None => self.send_join(chan)?,
593 }
594 }
595 let joined_chans = self.chanlists.read();
596 for chan in joined_chans
597 .keys()
598 .filter(|x| !config_chans.iter().any(|c| c == *x))
599 {
600 self.send_join(chan)?
601 }
602 }
603 Command::Response(Response::ERR_NICKNAMEINUSE, _)
604 | Command::Response(Response::ERR_ERRONEOUSNICKNAME, _) => {
605 let alt_nicks = self.config().alternate_nicknames();
606 let mut index = self.alt_nick_index.write();
607
608 if *index >= alt_nicks.len() {
609 return Err(error::Error::NoUsableNick);
610 } else {
611 self.send(NICK(alt_nicks[*index].to_owned()))?;
612 *index += 1;
613 }
614 }
615 _ => (),
616 }
617 Ok(())
618 }
619
620 fn send_nick_password(&self) -> error::Result<()> {
621 if self.config().nick_password().is_empty() {
622 Ok(())
623 } else {
624 let mut index = self.alt_nick_index.write();
625
626 if self.config().should_ghost() && *index != 0 {
627 let seq = match self.config().ghost_sequence() {
628 Some(seq) => seq,
629 None => &*self.default_ghost_sequence,
630 };
631
632 for s in seq {
633 self.send(NICKSERV(vec![
634 s.to_string(),
635 self.config().nickname()?.to_string(),
636 self.config().nick_password().to_string(),
637 ]))?;
638 }
639 *index = 0;
640 self.send(NICK(self.config().nickname()?.to_owned()))?
641 }
642
643 self.send(NICKSERV(vec![
644 "IDENTIFY".to_string(),
645 self.config().nick_password().to_string(),
646 ]))
647 }
648 }
649
650 fn send_umodes(&self) -> error::Result<()> {
651 if self.config().umodes().is_empty() {
652 Ok(())
653 } else {
654 self.send_mode(
655 self.current_nickname(),
656 &Mode::as_user_modes(
657 self.config()
658 .umodes()
659 .split(' ')
660 .collect::<Vec<_>>()
661 .as_ref(),
662 )
663 .map_err(|e| error::Error::InvalidMessage {
664 string: format!(
665 "MODE {} {}",
666 self.current_nickname(),
667 self.config().umodes()
668 ),
669 cause: e,
670 })?,
671 )
672 }
673 }
674
675 #[cfg(not(feature = "channel-lists"))]
676 fn handle_join(&self, _: &str, _: &str) {}
677
678 #[cfg(feature = "channel-lists")]
679 fn handle_join(&self, src: &str, chan: &str) {
680 if let Some(vec) = self.chanlists.write().get_mut(&chan.to_owned()) {
681 if !src.is_empty() {
682 vec.push(User::new(src))
683 }
684 }
685 }
686
687 #[cfg(not(feature = "channel-lists"))]
688 fn handle_part(&self, _: &str, _: &str) {}
689
690 #[cfg(feature = "channel-lists")]
691 fn handle_part(&self, src: &str, chan: &str) {
692 if let Some(vec) = self.chanlists.write().get_mut(&chan.to_owned()) {
693 if !src.is_empty() {
694 if let Some(n) = vec.iter().position(|x| x.get_nickname() == src) {
695 vec.swap_remove(n);
696 }
697 }
698 }
699 }
700
701 #[cfg(not(feature = "channel-lists"))]
702 fn handle_quit(&self, _: &str) {}
703
704 #[cfg(feature = "channel-lists")]
705 fn handle_quit(&self, src: &str) {
706 if src.is_empty() {
707 return;
708 }
709
710 for vec in self.chanlists.write().values_mut() {
711 if let Some(p) = vec.iter().position(|x| x.get_nickname() == src) {
712 vec.swap_remove(p);
713 }
714 }
715 }
716
717 #[cfg(not(feature = "channel-lists"))]
718 fn handle_nick_change(&self, _: &str, _: &str) {}
719
720 #[cfg(feature = "channel-lists")]
721 fn handle_nick_change(&self, old_nick: &str, new_nick: &str) {
722 if old_nick.is_empty() || new_nick.is_empty() {
723 return;
724 }
725
726 for (_, vec) in self.chanlists.write().iter_mut() {
727 if let Some(n) = vec.iter().position(|x| x.get_nickname() == old_nick) {
728 let new_entry = User::new(new_nick);
729 vec[n] = new_entry;
730 }
731 }
732 }
733
734 #[cfg(not(feature = "channel-lists"))]
735 fn handle_mode(&self, _: &str, _: &[Mode<ChannelMode>]) {}
736
737 #[cfg(feature = "channel-lists")]
738 fn handle_mode(&self, chan: &str, modes: &[Mode<ChannelMode>]) {
739 for mode in modes {
740 match *mode {
741 Mode::Plus(_, Some(ref user)) | Mode::Minus(_, Some(ref user)) => {
742 if let Some(vec) = self.chanlists.write().get_mut(chan) {
743 if let Some(n) = vec.iter().position(|x| x.get_nickname() == user) {
744 vec[n].update_access_level(mode)
745 }
746 }
747 }
748 _ => (),
749 }
750 }
751 }
752
753 #[cfg(not(feature = "channel-lists"))]
754 fn handle_namreply(&self, _: &[String]) {}
755
756 #[cfg(feature = "channel-lists")]
757 fn handle_namreply(&self, args: &[String]) {
758 if args.len() == 4 {
759 let chan = &args[2];
760 for user in args[3].split(' ') {
761 self.chanlists
762 .write()
763 .entry(chan.clone())
764 .or_insert_with(Vec::new)
765 .push(User::new(user))
766 }
767 }
768 }
769
770 #[cfg(feature = "ctcp")]
771 fn handle_ctcp(&self, resp: &str, tokens: &[&str]) -> error::Result<()> {
772 if tokens.is_empty() {
773 return Ok(());
774 }
775 if tokens[0].eq_ignore_ascii_case("FINGER") {
776 self.send_ctcp_internal(
777 resp,
778 &format!(
779 "FINGER :{} ({})",
780 self.config().real_name(),
781 self.config().username()
782 ),
783 )
784 } else if tokens[0].eq_ignore_ascii_case("VERSION") {
785 self.send_ctcp_internal(resp, &format!("VERSION {}", self.config().version()))
786 } else if tokens[0].eq_ignore_ascii_case("SOURCE") {
787 self.send_ctcp_internal(resp, &format!("SOURCE {}", self.config().source()))
788 } else if tokens[0].eq_ignore_ascii_case("PING") && tokens.len() > 1 {
789 self.send_ctcp_internal(resp, &format!("PING {}", tokens[1]))
790 } else if tokens[0].eq_ignore_ascii_case("TIME") {
791 self.send_ctcp_internal(resp, &format!("TIME :{}", Local::now().to_rfc2822()))
792 } else if tokens[0].eq_ignore_ascii_case("USERINFO") {
793 self.send_ctcp_internal(resp, &format!("USERINFO :{}", self.config().user_info()))
794 } else {
795 Ok(())
796 }
797 }
798
799 #[cfg(feature = "ctcp")]
800 fn send_ctcp_internal(&self, target: &str, msg: &str) -> error::Result<()> {
801 self.send_notice(target, format!("\u{001}{}\u{001}", msg))
802 }
803
804 #[cfg(not(feature = "ctcp"))]
805 fn handle_ctcp(&self, _: &str, _: &[&str]) -> error::Result<()> {
806 Ok(())
807 }
808
809 pub_state_base!();
810}
811
812#[derive(Debug, Clone)]
814pub struct Sender {
815 tx_outgoing: UnboundedSender<Message>,
816}
817
818impl Sender {
819 pub fn send<M: Into<Message>>(&self, msg: M) -> error::Result<()> {
821 Ok(self.tx_outgoing.send(msg.into())?)
822 }
823
824 pub_state_base!();
825 pub_sender_base!();
826}
827
828#[derive(Debug)]
832pub struct Outgoing {
833 sink: SplitSink<Connection, Message>,
834 stream: UnboundedReceiver<Message>,
835 buffered: Option<Message>,
836}
837
838impl Outgoing {
839 fn try_start_send(
840 &mut self,
841 cx: &mut Context<'_>,
842 message: Message,
843 ) -> Poll<Result<(), error::Error>> {
844 debug_assert!(self.buffered.is_none());
845
846 match Pin::new(&mut self.sink).poll_ready(cx)? {
847 Poll::Ready(()) => Poll::Ready(Pin::new(&mut self.sink).start_send(message)),
848 Poll::Pending => {
849 self.buffered = Some(message);
850 Poll::Pending
851 }
852 }
853 }
854}
855
856impl FusedFuture for Outgoing {
857 fn is_terminated(&self) -> bool {
858 false
861 }
862}
863
864impl Future for Outgoing {
865 type Output = error::Result<()>;
866
867 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
868 let this = &mut *self;
869
870 if let Some(message) = this.buffered.take() {
871 ready!(this.try_start_send(cx, message))?
872 }
873
874 loop {
875 match this.stream.poll_recv(cx) {
876 Poll::Ready(Some(message)) => ready!(this.try_start_send(cx, message))?,
877 Poll::Ready(None) => {
878 ready!(Pin::new(&mut this.sink).poll_flush(cx))?;
879 return Poll::Ready(Ok(()));
880 }
881 Poll::Pending => {
882 ready!(Pin::new(&mut this.sink).poll_flush(cx))?;
883 return Poll::Pending;
884 }
885 }
886 }
887 }
888}
889
890#[derive(Debug)]
894pub struct Client {
895 state: Arc<ClientState>,
897 incoming: Option<SplitStream<Connection>>,
898 outgoing: Option<Outgoing>,
899 sender: Sender,
900 #[cfg(test)]
901 view: Option<self::transport::LogView>,
903}
904
905impl Client {
906 pub async fn new<P: AsRef<Path>>(config: P) -> error::Result<Client> {
920 Client::from_config(Config::load(config)?).await
921 }
922
923 pub async fn from_config(config: Config) -> error::Result<Client> {
928 let (tx_outgoing, rx_outgoing) = mpsc::unbounded_channel();
929 let conn = Connection::new(&config, tx_outgoing.clone()).await?;
930
931 #[cfg(test)]
932 let view = conn.log_view();
933
934 let (sink, incoming) = conn.split();
935
936 let sender = Sender { tx_outgoing };
937
938 Ok(Client {
939 sender: sender.clone(),
940 state: Arc::new(ClientState::new(sender, config)),
941 incoming: Some(incoming),
942 outgoing: Some(Outgoing {
943 sink,
944 stream: rx_outgoing,
945 buffered: None,
946 }),
947 #[cfg(test)]
948 view,
949 })
950 }
951
952 #[cfg(test)]
954 fn log_view(&self) -> &self::transport::LogView {
955 self.view
956 .as_ref()
957 .expect("there should be a log during testing")
958 }
959
960 pub fn outgoing(&mut self) -> Option<Outgoing> {
965 self.outgoing.take()
966 }
967
968 pub fn sender(&self) -> Sender {
970 self.sender.clone()
971 }
972
973 fn config(&self) -> &Config {
975 &self.state.config
976 }
977
978 pub fn stream(&mut self) -> error::Result<ClientStream> {
986 let stream = self
987 .incoming
988 .take()
989 .ok_or(error::Error::StreamAlreadyConfigured)?;
990
991 Ok(ClientStream {
992 state: Arc::clone(&self.state),
993 stream,
994 outgoing: self.outgoing.take(),
995 })
996 }
997
998 #[cfg(feature = "channel-lists")]
1001 pub fn list_channels(&self) -> Option<Vec<String>> {
1002 Some(
1003 self.state
1004 .chanlists
1005 .read()
1006 .keys()
1007 .map(|k| k.to_owned())
1008 .collect(),
1009 )
1010 }
1011
1012 #[cfg(not(feature = "channel-lists"))]
1014 pub fn list_channels(&self) -> Option<Vec<String>> {
1015 None
1016 }
1017
1018 #[cfg(feature = "channel-lists")]
1038 pub fn list_users(&self, chan: &str) -> Option<Vec<User>> {
1039 self.state.chanlists.read().get(&chan.to_owned()).cloned()
1040 }
1041
1042 #[cfg(not(feature = "channel-lists"))]
1044 pub fn list_users(&self, _: &str) -> Option<Vec<User>> {
1045 None
1046 }
1047
1048 pub fn current_nickname(&self) -> &str {
1052 self.state.current_nickname()
1053 }
1054
1055 pub fn send<M: Into<Message>>(&self, msg: M) -> error::Result<()> {
1069 self.state.send(msg)
1070 }
1071
1072 pub fn identify(&self) -> error::Result<()> {
1074 self.send(CAP(None, END, None, None))?;
1076 if self.config().password() != "" {
1077 self.send(PASS(self.config().password().to_owned()))?;
1078 }
1079 self.send(NICK(self.config().nickname()?.to_owned()))?;
1080 self.send(USER(
1081 self.config().username().to_owned(),
1082 "0".to_owned(),
1083 self.config().real_name().to_owned(),
1084 ))?;
1085 Ok(())
1086 }
1087
1088 pub_state_base!();
1089 pub_sender_base!();
1090}
1091
1092#[cfg(test)]
1093mod test {
1094 use std::{collections::HashMap, default::Default, thread, time::Duration};
1095
1096 use super::Client;
1097 #[cfg(feature = "channel-lists")]
1098 use crate::client::data::User;
1099 use crate::{
1100 client::data::Config,
1101 error::Error,
1102 proto::{
1103 command::Command::{Raw, PRIVMSG},
1104 ChannelMode, IrcCodec, Mode, UserMode,
1105 },
1106 };
1107 use anyhow::Result;
1108 use futures::prelude::*;
1109
1110 pub fn test_config() -> Config {
1111 Config {
1112 owners: vec!["test".to_string()],
1113 nickname: Some("test".to_string()),
1114 alt_nicks: vec!["test2".to_string()],
1115 server: Some("irc.test.net".to_string()),
1116 channels: vec!["#test".to_string(), "#test2".to_string()],
1117 user_info: Some("Testing.".to_string()),
1118 use_mock_connection: true,
1119 ..Default::default()
1120 }
1121 }
1122
1123 pub fn get_client_value(client: Client) -> String {
1124 thread::sleep(Duration::from_millis(100));
1127 client
1128 .log_view()
1129 .sent()
1130 .unwrap()
1131 .iter()
1132 .fold(String::new(), |mut acc, msg| {
1133 acc.push_str(&IrcCodec::sanitize(msg.to_string()));
1137 acc
1138 })
1139 }
1140
1141 #[tokio::test]
1142 async fn stream() -> Result<()> {
1143 let exp = "PRIVMSG test :Hi!\r\nPRIVMSG test :This is a test!\r\n\
1144 :test!test@test JOIN #test\r\n";
1145
1146 let mut client = Client::from_config(Config {
1147 mock_initial_value: Some(exp.to_owned()),
1148 ..test_config()
1149 })
1150 .await?;
1151
1152 client.stream()?.collect().await?;
1153 Ok(())
1155 }
1156
1157 #[tokio::test]
1158 async fn handle_message() -> Result<()> {
1159 let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
1160 let mut client = Client::from_config(Config {
1161 mock_initial_value: Some(value.to_owned()),
1162 ..test_config()
1163 })
1164 .await?;
1165 client.stream()?.collect().await?;
1166 assert_eq!(
1167 &get_client_value(client)[..],
1168 "JOIN #test\r\nJOIN #test2\r\n"
1169 );
1170 Ok(())
1171 }
1172
1173 #[tokio::test]
1174 async fn handle_end_motd_with_nick_password() -> Result<()> {
1175 let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
1176 let mut client = Client::from_config(Config {
1177 mock_initial_value: Some(value.to_owned()),
1178 nick_password: Some("password".to_string()),
1179 channels: vec!["#test".to_string(), "#test2".to_string()],
1180 ..test_config()
1181 })
1182 .await?;
1183 client.stream()?.collect().await?;
1184 assert_eq!(
1185 &get_client_value(client)[..],
1186 "NICKSERV IDENTIFY password\r\nJOIN #test\r\n\
1187 JOIN #test2\r\n"
1188 );
1189 Ok(())
1190 }
1191
1192 #[tokio::test]
1193 async fn handle_end_motd_with_chan_keys() -> Result<()> {
1194 let value = ":irc.test.net 376 test :End of /MOTD command\r\n";
1195 let mut client = Client::from_config(Config {
1196 mock_initial_value: Some(value.to_owned()),
1197 nickname: Some("test".to_string()),
1198 channels: vec!["#test".to_string(), "#test2".to_string()],
1199 channel_keys: {
1200 let mut map = HashMap::new();
1201 map.insert("#test2".to_string(), "password".to_string());
1202 map
1203 },
1204 ..test_config()
1205 })
1206 .await?;
1207 client.stream()?.collect().await?;
1208 assert_eq!(
1209 &get_client_value(client)[..],
1210 "JOIN #test\r\nJOIN #test2 password\r\n"
1211 );
1212 Ok(())
1213 }
1214
1215 #[tokio::test]
1216 async fn handle_end_motd_with_ghost() -> Result<()> {
1217 let value = ":irc.test.net 433 * test :Nickname is already in use.\r\n\
1218 :irc.test.net 376 test2 :End of /MOTD command.\r\n";
1219 let mut client = Client::from_config(Config {
1220 mock_initial_value: Some(value.to_owned()),
1221 nickname: Some("test".to_string()),
1222 alt_nicks: vec!["test2".to_string()],
1223 nick_password: Some("password".to_string()),
1224 channels: vec!["#test".to_string(), "#test2".to_string()],
1225 should_ghost: true,
1226 ..test_config()
1227 })
1228 .await?;
1229 client.stream()?.collect().await?;
1230 assert_eq!(
1231 &get_client_value(client)[..],
1232 "NICK test2\r\nNICKSERV GHOST test password\r\n\
1233 NICK test\r\nNICKSERV IDENTIFY password\r\nJOIN #test\r\nJOIN #test2\r\n"
1234 );
1235 Ok(())
1236 }
1237
1238 #[tokio::test]
1239 async fn handle_end_motd_with_ghost_seq() -> Result<()> {
1240 let value = ":irc.test.net 433 * test :Nickname is already in use.\r\n\
1241 :irc.test.net 376 test2 :End of /MOTD command.\r\n";
1242 let mut client = Client::from_config(Config {
1243 mock_initial_value: Some(value.to_owned()),
1244 nickname: Some("test".to_string()),
1245 alt_nicks: vec!["test2".to_string()],
1246 nick_password: Some("password".to_string()),
1247 channels: vec!["#test".to_string(), "#test2".to_string()],
1248 should_ghost: true,
1249 ghost_sequence: Some(vec!["RECOVER".to_string(), "RELEASE".to_string()]),
1250 ..test_config()
1251 })
1252 .await?;
1253 client.stream()?.collect().await?;
1254 assert_eq!(
1255 &get_client_value(client)[..],
1256 "NICK test2\r\nNICKSERV RECOVER test password\
1257 \r\nNICKSERV RELEASE test password\r\nNICK test\r\nNICKSERV IDENTIFY password\
1258 \r\nJOIN #test\r\nJOIN #test2\r\n"
1259 );
1260 Ok(())
1261 }
1262
1263 #[tokio::test]
1264 async fn handle_end_motd_with_umodes() -> Result<()> {
1265 let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
1266 let mut client = Client::from_config(Config {
1267 mock_initial_value: Some(value.to_owned()),
1268 nickname: Some("test".to_string()),
1269 umodes: Some("+B".to_string()),
1270 channels: vec!["#test".to_string(), "#test2".to_string()],
1271 ..test_config()
1272 })
1273 .await?;
1274 client.stream()?.collect().await?;
1275 assert_eq!(
1276 &get_client_value(client)[..],
1277 "MODE test +B\r\nJOIN #test\r\nJOIN #test2\r\n"
1278 );
1279 Ok(())
1280 }
1281
1282 #[tokio::test]
1283 async fn nickname_in_use() -> Result<()> {
1284 let value = ":irc.test.net 433 * test :Nickname is already in use.\r\n";
1285 let mut client = Client::from_config(Config {
1286 mock_initial_value: Some(value.to_owned()),
1287 ..test_config()
1288 })
1289 .await?;
1290 client.stream()?.collect().await?;
1291 assert_eq!(&get_client_value(client)[..], "NICK test2\r\n");
1292 Ok(())
1293 }
1294
1295 #[tokio::test]
1296 async fn ran_out_of_nicknames() -> Result<()> {
1297 let value = ":irc.test.net 433 * test :Nickname is already in use.\r\n\
1298 :irc.test.net 433 * test2 :Nickname is already in use.\r\n";
1299 let mut client = Client::from_config(Config {
1300 mock_initial_value: Some(value.to_owned()),
1301 ..test_config()
1302 })
1303 .await?;
1304 let res = client.stream()?.try_collect::<Vec<_>>().await;
1305 if let Err(Error::NoUsableNick) = res {
1306 } else {
1307 panic!("expected error when no valid nicks were specified")
1308 }
1309 Ok(())
1310 }
1311
1312 #[tokio::test]
1313 async fn send() -> Result<()> {
1314 let mut client = Client::from_config(test_config()).await?;
1315 assert!(client
1316 .send(PRIVMSG("#test".to_string(), "Hi there!".to_string()))
1317 .is_ok());
1318 client.stream()?.collect().await?;
1319 assert_eq!(
1320 &get_client_value(client)[..],
1321 "PRIVMSG #test :Hi there!\r\n"
1322 );
1323 Ok(())
1324 }
1325
1326 #[tokio::test]
1327 async fn send_no_newline_injection() -> Result<()> {
1328 let mut client = Client::from_config(test_config()).await?;
1329 assert!(client
1330 .send(PRIVMSG(
1331 "#test".to_string(),
1332 "Hi there!\r\nJOIN #bad".to_string()
1333 ))
1334 .is_ok());
1335 client.stream()?.collect().await?;
1336 assert_eq!(
1337 &get_client_value(client)[..],
1338 "PRIVMSG #test :Hi there!\r\n"
1339 );
1340 Ok(())
1341 }
1342
1343 #[tokio::test]
1344 async fn send_raw_is_really_raw() -> Result<()> {
1345 let mut client = Client::from_config(test_config()).await?;
1346 assert!(client
1347 .send(Raw("PASS".to_owned(), vec!["password".to_owned()]))
1348 .is_ok());
1349 assert!(client
1350 .send(Raw("NICK".to_owned(), vec!["test".to_owned()]))
1351 .is_ok());
1352 client.stream()?.collect().await?;
1353 assert_eq!(
1354 &get_client_value(client)[..],
1355 "PASS password\r\nNICK test\r\n"
1356 );
1357 Ok(())
1358 }
1359
1360 #[tokio::test]
1361 #[cfg(feature = "channel-lists")]
1362 async fn channel_tracking_names() -> Result<()> {
1363 let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
1364 let mut client = Client::from_config(Config {
1365 mock_initial_value: Some(value.to_owned()),
1366 ..test_config()
1367 })
1368 .await?;
1369 client.stream()?.collect().await?;
1370 assert_eq!(client.list_channels().unwrap(), vec!["#test".to_owned()]);
1371 Ok(())
1372 }
1373
1374 #[tokio::test]
1375 #[cfg(feature = "channel-lists")]
1376 async fn channel_tracking_names_part() -> Result<()> {
1377 use crate::proto::command::Command::PART;
1378
1379 let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
1380 let mut client = Client::from_config(Config {
1381 mock_initial_value: Some(value.to_owned()),
1382 ..test_config()
1383 })
1384 .await?;
1385
1386 client.stream()?.collect().await?;
1387
1388 assert_eq!(client.list_channels(), Some(vec!["#test".to_owned()]));
1389 let _ = client.send(PART("#test".to_string(), None));
1392 assert_eq!(client.list_channels(), Some(vec![]));
1393 Ok(())
1394 }
1395
1396 #[tokio::test]
1397 #[cfg(feature = "channel-lists")]
1398 async fn user_tracking_names() -> Result<()> {
1399 let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
1400 let mut client = Client::from_config(Config {
1401 mock_initial_value: Some(value.to_owned()),
1402 ..test_config()
1403 })
1404 .await?;
1405 client.stream()?.collect().await?;
1406 assert_eq!(
1407 client.list_users("#test").unwrap(),
1408 vec![User::new("test"), User::new("~owner"), User::new("&admin")]
1409 );
1410 Ok(())
1411 }
1412
1413 #[tokio::test]
1414 #[cfg(feature = "channel-lists")]
1415 async fn user_tracking_names_join() -> Result<()> {
1416 let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n\
1417 :test2!test@test JOIN #test\r\n";
1418 let mut client = Client::from_config(Config {
1419 mock_initial_value: Some(value.to_owned()),
1420 ..test_config()
1421 })
1422 .await?;
1423 client.stream()?.collect().await?;
1424 assert_eq!(
1425 client.list_users("#test").unwrap(),
1426 vec![
1427 User::new("test"),
1428 User::new("~owner"),
1429 User::new("&admin"),
1430 User::new("test2"),
1431 ]
1432 );
1433 Ok(())
1434 }
1435
1436 #[tokio::test]
1437 #[cfg(feature = "channel-lists")]
1438 async fn user_tracking_names_kick() -> Result<()> {
1439 let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n\
1440 :owner!test@test KICK #test test\r\n";
1441 let mut client = Client::from_config(Config {
1442 mock_initial_value: Some(value.to_owned()),
1443 ..test_config()
1444 })
1445 .await?;
1446 client.stream()?.collect().await?;
1447 assert_eq!(
1448 client.list_users("#test").unwrap(),
1449 vec![User::new("&admin"), User::new("~owner"),]
1450 );
1451 Ok(())
1452 }
1453
1454 #[tokio::test]
1455 #[cfg(feature = "channel-lists")]
1456 async fn user_tracking_names_part() -> Result<()> {
1457 let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n\
1458 :owner!test@test PART #test\r\n";
1459 let mut client = Client::from_config(Config {
1460 mock_initial_value: Some(value.to_owned()),
1461 ..test_config()
1462 })
1463 .await?;
1464 client.stream()?.collect().await?;
1465 assert_eq!(
1466 client.list_users("#test").unwrap(),
1467 vec![User::new("test"), User::new("&admin")]
1468 );
1469 Ok(())
1470 }
1471
1472 #[tokio::test]
1473 #[cfg(feature = "channel-lists")]
1474 async fn user_tracking_names_mode() -> Result<()> {
1475 let value = ":irc.test.net 353 test = #test :+test ~owner &admin\r\n\
1476 :test!test@test MODE #test +o test\r\n";
1477 let mut client = Client::from_config(Config {
1478 mock_initial_value: Some(value.to_owned()),
1479 ..test_config()
1480 })
1481 .await?;
1482 client.stream()?.collect().await?;
1483 assert_eq!(
1484 client.list_users("#test").unwrap(),
1485 vec![User::new("@test"), User::new("~owner"), User::new("&admin")]
1486 );
1487 let mut exp = User::new("@test");
1488 exp.update_access_level(&Mode::Plus(ChannelMode::Voice, None));
1489 assert_eq!(
1490 client.list_users("#test").unwrap()[0].highest_access_level(),
1491 exp.highest_access_level()
1492 );
1493 let mut levels = client.list_users("#test").unwrap()[0].access_levels();
1496 levels.retain(|l| exp.access_levels().contains(l));
1497 assert_eq!(levels.len(), exp.access_levels().len());
1498 Ok(())
1499 }
1500
1501 #[tokio::test]
1502 #[cfg(not(feature = "channel-lists"))]
1503 async fn no_user_tracking() -> Result<()> {
1504 let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
1505 let mut client = Client::from_config(Config {
1506 mock_initial_value: Some(value.to_owned()),
1507 ..test_config()
1508 })
1509 .await?;
1510 client.stream()?.collect().await?;
1511 assert!(client.list_users("#test").is_none());
1512 Ok(())
1513 }
1514
1515 #[tokio::test]
1516 async fn handle_single_soh() -> Result<()> {
1517 let value = ":test!test@test PRIVMSG #test :\u{001}\r\n";
1518 let mut client = Client::from_config(Config {
1519 mock_initial_value: Some(value.to_owned()),
1520 nickname: Some("test".to_string()),
1521 channels: vec!["#test".to_string(), "#test2".to_string()],
1522 ..test_config()
1523 })
1524 .await?;
1525 client.stream()?.collect().await?;
1526 Ok(())
1527 }
1528
1529 #[tokio::test]
1530 #[cfg(feature = "ctcp")]
1531 async fn finger_response() -> Result<()> {
1532 let value = ":test!test@test PRIVMSG test :\u{001}FINGER\u{001}\r\n";
1533 let mut client = Client::from_config(Config {
1534 mock_initial_value: Some(value.to_owned()),
1535 ..test_config()
1536 })
1537 .await?;
1538 client.stream()?.collect().await?;
1539 assert_eq!(
1540 &get_client_value(client)[..],
1541 "NOTICE test :\u{001}FINGER :test (test)\u{001}\r\n"
1542 );
1543 Ok(())
1544 }
1545
1546 #[tokio::test]
1547 #[cfg(feature = "ctcp")]
1548 async fn version_response() -> Result<()> {
1549 let value = ":test!test@test PRIVMSG test :\u{001}VERSION\u{001}\r\n";
1550 let mut client = Client::from_config(Config {
1551 mock_initial_value: Some(value.to_owned()),
1552 ..test_config()
1553 })
1554 .await?;
1555 client.stream()?.collect().await?;
1556 assert_eq!(
1557 &get_client_value(client)[..],
1558 &format!(
1559 "NOTICE test :\u{001}VERSION {}\u{001}\r\n",
1560 crate::VERSION_STR,
1561 )
1562 );
1563 Ok(())
1564 }
1565
1566 #[tokio::test]
1567 #[cfg(feature = "ctcp")]
1568 async fn source_response() -> Result<()> {
1569 let value = ":test!test@test PRIVMSG test :\u{001}SOURCE\u{001}\r\n";
1570 let mut client = Client::from_config(Config {
1571 mock_initial_value: Some(value.to_owned()),
1572 ..test_config()
1573 })
1574 .await?;
1575 client.stream()?.collect().await?;
1576 assert_eq!(
1577 &get_client_value(client)[..],
1578 "NOTICE test :\u{001}SOURCE https://github.com/aatxe/irc\u{001}\r\n"
1579 );
1580 Ok(())
1581 }
1582
1583 #[tokio::test]
1584 #[cfg(feature = "ctcp")]
1585 async fn ctcp_ping_response() -> Result<()> {
1586 let value = ":test!test@test PRIVMSG test :\u{001}PING test\u{001}\r\n";
1587 let mut client = Client::from_config(Config {
1588 mock_initial_value: Some(value.to_owned()),
1589 ..test_config()
1590 })
1591 .await?;
1592 client.stream()?.collect().await?;
1593 assert_eq!(
1594 &get_client_value(client)[..],
1595 "NOTICE test :\u{001}PING test\u{001}\r\n"
1596 );
1597 Ok(())
1598 }
1599
1600 #[tokio::test]
1601 #[cfg(feature = "ctcp")]
1602 async fn time_response() -> Result<()> {
1603 let value = ":test!test@test PRIVMSG test :\u{001}TIME\u{001}\r\n";
1604 let mut client = Client::from_config(Config {
1605 mock_initial_value: Some(value.to_owned()),
1606 ..test_config()
1607 })
1608 .await?;
1609 client.stream()?.collect().await?;
1610 let val = get_client_value(client);
1611 assert!(val.starts_with("NOTICE test :\u{001}TIME :"));
1612 assert!(val.ends_with("\u{001}\r\n"));
1613 Ok(())
1614 }
1615
1616 #[tokio::test]
1617 #[cfg(feature = "ctcp")]
1618 async fn user_info_response() -> Result<()> {
1619 let value = ":test!test@test PRIVMSG test :\u{001}USERINFO\u{001}\r\n";
1620 let mut client = Client::from_config(Config {
1621 mock_initial_value: Some(value.to_owned()),
1622 ..test_config()
1623 })
1624 .await?;
1625 client.stream()?.collect().await?;
1626 assert_eq!(
1627 &get_client_value(client)[..],
1628 "NOTICE test :\u{001}USERINFO :Testing.\u{001}\
1629 \r\n"
1630 );
1631 Ok(())
1632 }
1633
1634 #[tokio::test]
1635 #[cfg(feature = "ctcp")]
1636 async fn ctcp_ping_no_timestamp() -> Result<()> {
1637 let value = ":test!test@test PRIVMSG test \u{001}PING\u{001}\r\n";
1638 let mut client = Client::from_config(Config {
1639 mock_initial_value: Some(value.to_owned()),
1640 ..test_config()
1641 })
1642 .await?;
1643 client.stream()?.collect().await?;
1644 assert_eq!(&get_client_value(client)[..], "");
1645 Ok(())
1646 }
1647
1648 #[tokio::test]
1649 async fn identify() -> Result<()> {
1650 let mut client = Client::from_config(test_config()).await?;
1651 client.identify()?;
1652 client.stream()?.collect().await?;
1653 assert_eq!(
1654 &get_client_value(client)[..],
1655 "CAP END\r\nNICK test\r\n\
1656 USER test 0 * test\r\n"
1657 );
1658 Ok(())
1659 }
1660
1661 #[tokio::test]
1662 async fn identify_with_password() -> Result<()> {
1663 let mut client = Client::from_config(Config {
1664 nickname: Some("test".to_string()),
1665 password: Some("password".to_string()),
1666 ..test_config()
1667 })
1668 .await?;
1669 client.identify()?;
1670 client.stream()?.collect().await?;
1671 assert_eq!(
1672 &get_client_value(client)[..],
1673 "CAP END\r\nPASS password\r\nNICK test\r\n\
1674 USER test 0 * test\r\n"
1675 );
1676 Ok(())
1677 }
1678
1679 #[tokio::test]
1680 async fn send_pong() -> Result<()> {
1681 let mut client = Client::from_config(test_config()).await?;
1682 client.send_pong("irc.test.net")?;
1683 client.stream()?.collect().await?;
1684 assert_eq!(&get_client_value(client)[..], "PONG irc.test.net\r\n");
1685 Ok(())
1686 }
1687
1688 #[tokio::test]
1689 async fn send_join() -> Result<()> {
1690 let mut client = Client::from_config(test_config()).await?;
1691 client.send_join("#test,#test2,#test3")?;
1692 client.stream()?.collect().await?;
1693 assert_eq!(
1694 &get_client_value(client)[..],
1695 "JOIN #test,#test2,#test3\r\n"
1696 );
1697 Ok(())
1698 }
1699
1700 #[tokio::test]
1701 async fn send_part() -> Result<()> {
1702 let mut client = Client::from_config(test_config()).await?;
1703 client.send_part("#test")?;
1704 client.stream()?.collect().await?;
1705 assert_eq!(&get_client_value(client)[..], "PART #test\r\n");
1706 Ok(())
1707 }
1708
1709 #[tokio::test]
1710 async fn send_oper() -> Result<()> {
1711 let mut client = Client::from_config(test_config()).await?;
1712 client.send_oper("test", "test")?;
1713 client.stream()?.collect().await?;
1714 assert_eq!(&get_client_value(client)[..], "OPER test test\r\n");
1715 Ok(())
1716 }
1717
1718 #[tokio::test]
1719 async fn send_privmsg() -> Result<()> {
1720 let mut client = Client::from_config(test_config()).await?;
1721 client.send_privmsg("#test", "Hi, everybody!")?;
1722 client.stream()?.collect().await?;
1723 assert_eq!(
1724 &get_client_value(client)[..],
1725 "PRIVMSG #test :Hi, everybody!\r\n"
1726 );
1727 Ok(())
1728 }
1729
1730 #[tokio::test]
1731 async fn send_notice() -> Result<()> {
1732 let mut client = Client::from_config(test_config()).await?;
1733 client.send_notice("#test", "Hi, everybody!")?;
1734 client.stream()?.collect().await?;
1735 assert_eq!(
1736 &get_client_value(client)[..],
1737 "NOTICE #test :Hi, everybody!\r\n"
1738 );
1739 Ok(())
1740 }
1741
1742 #[tokio::test]
1743 async fn send_topic_no_topic() -> Result<()> {
1744 let mut client = Client::from_config(test_config()).await?;
1745 client.send_topic("#test", "")?;
1746 client.stream()?.collect().await?;
1747 assert_eq!(&get_client_value(client)[..], "TOPIC #test\r\n");
1748 Ok(())
1749 }
1750
1751 #[tokio::test]
1752 async fn send_topic() -> Result<()> {
1753 let mut client = Client::from_config(test_config()).await?;
1754 client.send_topic("#test", "Testing stuff.")?;
1755 client.stream()?.collect().await?;
1756 assert_eq!(
1757 &get_client_value(client)[..],
1758 "TOPIC #test :Testing stuff.\r\n"
1759 );
1760 Ok(())
1761 }
1762
1763 #[tokio::test]
1764 async fn send_kill() -> Result<()> {
1765 let mut client = Client::from_config(test_config()).await?;
1766 client.send_kill("test", "Testing kills.")?;
1767 client.stream()?.collect().await?;
1768 assert_eq!(
1769 &get_client_value(client)[..],
1770 "KILL test :Testing kills.\r\n"
1771 );
1772 Ok(())
1773 }
1774
1775 #[tokio::test]
1776 async fn send_kick_no_message() -> Result<()> {
1777 let mut client = Client::from_config(test_config()).await?;
1778 client.send_kick("#test", "test", "")?;
1779 client.stream()?.collect().await?;
1780 assert_eq!(&get_client_value(client)[..], "KICK #test test\r\n");
1781 Ok(())
1782 }
1783
1784 #[tokio::test]
1785 async fn send_kick() -> Result<()> {
1786 let mut client = Client::from_config(test_config()).await?;
1787 client.send_kick("#test", "test", "Testing kicks.")?;
1788 client.stream()?.collect().await?;
1789 assert_eq!(
1790 &get_client_value(client)[..],
1791 "KICK #test test :Testing kicks.\r\n"
1792 );
1793 Ok(())
1794 }
1795
1796 #[tokio::test]
1797 async fn send_mode_no_modeparams() -> Result<()> {
1798 let mut client = Client::from_config(test_config()).await?;
1799 client.send_mode("#test", &[Mode::Plus(ChannelMode::InviteOnly, None)])?;
1800 client.stream()?.collect().await?;
1801 assert_eq!(&get_client_value(client)[..], "MODE #test +i\r\n");
1802 Ok(())
1803 }
1804
1805 #[tokio::test]
1806 async fn send_mode() -> Result<()> {
1807 let mut client = Client::from_config(test_config()).await?;
1808 client.send_mode(
1809 "#test",
1810 &[
1811 Mode::Plus(ChannelMode::Oper, Some("test".to_owned())),
1812 Mode::Minus(ChannelMode::Oper, Some("test2".to_owned())),
1813 ],
1814 )?;
1815 client.stream()?.collect().await?;
1816 assert_eq!(
1817 &get_client_value(client)[..],
1818 "MODE #test +o-o test test2\r\n"
1819 );
1820 Ok(())
1821 }
1822
1823 #[tokio::test]
1824 async fn send_umode() -> Result<()> {
1825 let mut client = Client::from_config(test_config()).await?;
1826 client.send_mode(
1827 "test",
1828 &[
1829 Mode::Plus(UserMode::Invisible, None),
1830 Mode::Plus(UserMode::MaskedHost, None),
1831 ],
1832 )?;
1833 client.stream()?.collect().await?;
1834 assert_eq!(&get_client_value(client)[..], "MODE test +i+x\r\n");
1835 Ok(())
1836 }
1837
1838 #[tokio::test]
1839 async fn send_samode_no_modeparams() -> Result<()> {
1840 let mut client = Client::from_config(test_config()).await?;
1841 client.send_samode("#test", "+i", "")?;
1842 client.stream()?.collect().await?;
1843 assert_eq!(&get_client_value(client)[..], "SAMODE #test +i\r\n");
1844 Ok(())
1845 }
1846
1847 #[tokio::test]
1848 async fn send_samode() -> Result<()> {
1849 let mut client = Client::from_config(test_config()).await?;
1850 client.send_samode("#test", "+o", "test")?;
1851 client.stream()?.collect().await?;
1852 assert_eq!(&get_client_value(client)[..], "SAMODE #test +o test\r\n");
1853 Ok(())
1854 }
1855
1856 #[tokio::test]
1857 async fn send_sanick() -> Result<()> {
1858 let mut client = Client::from_config(test_config()).await?;
1859 client.send_sanick("test", "test2")?;
1860 client.stream()?.collect().await?;
1861 assert_eq!(&get_client_value(client)[..], "SANICK test test2\r\n");
1862 Ok(())
1863 }
1864
1865 #[tokio::test]
1866 async fn send_invite() -> Result<()> {
1867 let mut client = Client::from_config(test_config()).await?;
1868 client.send_invite("test", "#test")?;
1869 client.stream()?.collect().await?;
1870 assert_eq!(&get_client_value(client)[..], "INVITE test #test\r\n");
1871 Ok(())
1872 }
1873
1874 #[tokio::test]
1875 #[cfg(feature = "ctcp")]
1876 async fn send_ctcp() -> Result<()> {
1877 let mut client = Client::from_config(test_config()).await?;
1878 client.send_ctcp("test", "LINE1\r\nLINE2\r\nLINE3")?;
1879 client.stream()?.collect().await?;
1880 assert_eq!(
1881 &get_client_value(client)[..],
1882 "PRIVMSG test \u{001}LINE1\u{001}\r\nPRIVMSG test \u{001}LINE2\u{001}\r\nPRIVMSG test \u{001}LINE3\u{001}\r\n"
1883 );
1884 Ok(())
1885 }
1886
1887 #[tokio::test]
1888 #[cfg(feature = "ctcp")]
1889 async fn send_action() -> Result<()> {
1890 let mut client = Client::from_config(test_config()).await?;
1891 client.send_action("test", "tests.")?;
1892 client.stream()?.collect().await?;
1893 assert_eq!(
1894 &get_client_value(client)[..],
1895 "PRIVMSG test :\u{001}ACTION tests.\u{001}\r\n"
1896 );
1897 Ok(())
1898 }
1899
1900 #[tokio::test]
1901 #[cfg(feature = "ctcp")]
1902 async fn send_finger() -> Result<()> {
1903 let mut client = Client::from_config(test_config()).await?;
1904 client.send_finger("test")?;
1905 client.stream()?.collect().await?;
1906 assert_eq!(
1907 &get_client_value(client)[..],
1908 "PRIVMSG test \u{001}FINGER\u{001}\r\n"
1909 );
1910 Ok(())
1911 }
1912
1913 #[tokio::test]
1914 #[cfg(feature = "ctcp")]
1915 async fn send_version() -> Result<()> {
1916 let mut client = Client::from_config(test_config()).await?;
1917 client.send_version("test")?;
1918 client.stream()?.collect().await?;
1919 assert_eq!(
1920 &get_client_value(client)[..],
1921 "PRIVMSG test \u{001}VERSION\u{001}\r\n"
1922 );
1923 Ok(())
1924 }
1925
1926 #[tokio::test]
1927 #[cfg(feature = "ctcp")]
1928 async fn send_source() -> Result<()> {
1929 let mut client = Client::from_config(test_config()).await?;
1930 client.send_source("test")?;
1931 client.stream()?.collect().await?;
1932 assert_eq!(
1933 &get_client_value(client)[..],
1934 "PRIVMSG test \u{001}SOURCE\u{001}\r\n"
1935 );
1936 Ok(())
1937 }
1938
1939 #[tokio::test]
1940 #[cfg(feature = "ctcp")]
1941 async fn send_user_info() -> Result<()> {
1942 let mut client = Client::from_config(test_config()).await?;
1943 client.send_user_info("test")?;
1944 client.stream()?.collect().await?;
1945 assert_eq!(
1946 &get_client_value(client)[..],
1947 "PRIVMSG test \u{001}USERINFO\u{001}\r\n"
1948 );
1949 Ok(())
1950 }
1951
1952 #[tokio::test]
1953 #[cfg(feature = "ctcp")]
1954 async fn send_ctcp_ping() -> Result<()> {
1955 let mut client = Client::from_config(test_config()).await?;
1956 client.send_ctcp_ping("test")?;
1957 client.stream()?.collect().await?;
1958 let val = get_client_value(client);
1959 println!("{}", val);
1960 assert!(val.starts_with("PRIVMSG test :\u{001}PING "));
1961 assert!(val.ends_with("\u{001}\r\n"));
1962 Ok(())
1963 }
1964
1965 #[tokio::test]
1966 #[cfg(feature = "ctcp")]
1967 async fn send_time() -> Result<()> {
1968 let mut client = Client::from_config(test_config()).await?;
1969 client.send_time("test")?;
1970 client.stream()?.collect().await?;
1971 assert_eq!(
1972 &get_client_value(client)[..],
1973 "PRIVMSG test \u{001}TIME\u{001}\r\n"
1974 );
1975 Ok(())
1976 }
1977}